Libraries

We load the packages relevant for the exercise.

library(FactoMineR)
library(tidyr)
library(dplyr)
library(tidyverse)
library(magrittr)
library(ggplot2)
library(ggpubr)
library(factoextra)
library(gridExtra)
library(moments)

Screw Caps Data

The data ScrewCap.csv contains 195 lots of screw caps described by 11 variables. Diameter, weight, length are the physical characteristics of the cap; nb.of.pieces corresponds to the number of elements of the cap; Mature.volume corresponds to the number of caps ordered and bought by the company (number in the lot).

We first explore the dataset -

raw_data <- read.table("ScrewCaps.csv",header=TRUE, sep=",", dec=".", row.names=1)
head(raw_data)
dim(raw_data)
[1] 195  11
summary(raw_data)
       Supplier      Diameter          weight       nb.of.pieces        Shape     Impermeability        Finishing   Mature.Volume    Raw.Material     Price       
 Supplier A: 31   Min.   :0.4458   Min.   :0.610   Min.   : 2.000   Shape 1:134   Type 1:172     Hot Printing: 62   Min.   :  1000   ABS: 21      Min.   : 6.477  
 Supplier B:150   1st Qu.:0.7785   1st Qu.:1.083   1st Qu.: 3.000   Shape 2: 45   Type 2: 23     Lacquering  :133   1st Qu.: 15000   PP :148      1st Qu.:11.807  
 Supplier C: 14   Median :1.0120   Median :1.400   Median : 4.000   Shape 3:  8                                     Median : 45000   PS : 26      Median :14.384  
                  Mean   :1.2843   Mean   :1.701   Mean   : 4.113   Shape 4:  8                                     Mean   : 96930                Mean   :16.444  
                  3rd Qu.:1.2886   3rd Qu.:1.704   3rd Qu.: 5.000                                                   3rd Qu.:115000                3rd Qu.:18.902  
                  Max.   :5.3950   Max.   :7.112   Max.   :10.000                                                   Max.   :800000                Max.   :46.610  
     Length      
 Min.   : 3.369  
 1st Qu.: 6.161  
 Median : 8.086  
 Mean   :10.247  
 3rd Qu.:10.340  
 Max.   :43.359  

2) We start with univariate and bivariate descriptive statistics. Using appropriate plot(s) or summaries answer the following questions.

a) How is the distribution of the Price? Comment your plot with respect to the quartiles of the Price.

From the quantile data, the summary statistics are given by: median, 1Q and 3Q as 14.432, 11.864 and 19.04 respectively.

The plots, the kurtosis and the skewness parameters suggest the price follows a bimodal distribution that is “skewed right”.

The major mode is around 14 and the antimode is around 29. Furthermore, 50% of the prices in the range 11.864 and 19.04. This is consistent with graph where the majority of the density is concentrated inside this range and a long right tail of prices outside.

The boxplot supports this analyis and suggests the values in the tail are outliers.

price_density <- ggdensity(raw_data,x="Price",y = "..count..",
                        color="darkblue",
                        fill="lightblue",size=0.5, 
                        alpha=0.2, 
                        title = "Screw Cap Price Distribution", 
                        linetype = "solid", add = c("median"))+ font("title", size = 12,face="bold")
  
  
price_boxplot <- ggboxplot(raw_data$Price, width = 0.1, fill ="lightgray", outlier.colour = "darkblue", outlier.shape=4.2, ylab = "Price", xlab = "Screw Caps" , title = "Price Box Plot") + rotate() + font("title", size = 12,face="bold")
price_quantile <- quantile(raw_data$Price)
ggarrange(price_density, price_boxplot, ncol = 1, nrow = 2)

price_quantile
       0%       25%       50%       75%      100% 
 6.477451 11.807022 14.384413 18.902429 46.610372 
skewness(raw_data$Price)
[1] 1.706151
kurtosis(raw_data$Price)
[1] 6.395453

b) Does the Price depend on the Length? weight?

We examine Price vs. Length, log(Price) vs. log(Length); Price vs. weight, log(Price) vs. log(weight) and provide the summary for each.

The plots suggest somewhat of a relationship between the variables and observing the R-sqaured values and the results of the F and T tests confirm this to a high degree of significance.

price_length <- ggplot(raw_data, aes(x=Length, y=Price)) + geom_point() + geom_smooth(method=lm, color="darkgreen")+ theme_minimal()
price_length_log <- ggplot(raw_data, aes(x=log(Length), y=log(Price))) + geom_point() + geom_smooth(method=lm, color="darkgreen")+ theme_minimal()
price_weight <- ggplot(raw_data, aes(x=weight, y=Price)) + geom_point() + geom_smooth(method=lm,color="red")+theme_minimal()
price_weight_log <- ggplot(raw_data, aes(x=log(weight), y=log(Price))) + geom_point() + geom_smooth(method=lm,color="red")+theme_minimal()
ggarrange(ggarrange(price_length, price_length_log, ncol = 2, nrow = 1), ggarrange(price_weight, price_weight_log, ncol = 2, nrow = 1), ncol = 1, nrow = 2)

summary(lm(formula = Price ~ Length, raw_data))

Call:
lm(formula = Price ~ Length, data = raw_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-13.901  -2.854  -0.741   1.931  16.181 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  8.94613    0.50918   17.57   <2e-16 ***
Length       0.73168    0.03953   18.51   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.308 on 193 degrees of freedom
Multiple R-squared:  0.6397,    Adjusted R-squared:  0.6378 
F-statistic: 342.6 on 1 and 193 DF,  p-value: < 2.2e-16
summary(lm(formula = log(Price) ~ log(Length), raw_data))

Call:
lm(formula = log(Price) ~ log(Length), data = raw_data)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.70368 -0.15501 -0.01661  0.15170  0.59211 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.56380    0.07278   21.49   <2e-16 ***
log(Length)  0.53875    0.03282   16.42   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2466 on 193 degrees of freedom
Multiple R-squared:  0.5827,    Adjusted R-squared:  0.5805 
F-statistic: 269.5 on 1 and 193 DF,  p-value: < 2.2e-16
summary(lm(formula = Price ~ weight, raw_data))

Call:
lm(formula = Price ~ weight, data = raw_data)

Residuals:
     Min       1Q   Median       3Q      Max 
-14.7993  -2.6207  -0.6631   2.5396  13.8357 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   8.2275     0.5602   14.69   <2e-16 ***
weight        4.8312     0.2718   17.78   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.419 on 193 degrees of freedom
Multiple R-squared:  0.6208,    Adjusted R-squared:  0.6189 
F-statistic:   316 on 1 and 193 DF,  p-value: < 2.2e-16
summary(lm(formula = log(Price) ~ log(weight), raw_data))

Call:
lm(formula = log(Price) ~ log(weight), data = raw_data)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.71123 -0.15340 -0.01343  0.17735  0.69552 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  2.50618    0.02333  107.42   <2e-16 ***
log(weight)  0.56453    0.03718   15.18   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2577 on 193 degrees of freedom
Multiple R-squared:  0.5443,    Adjusted R-squared:  0.5419 
F-statistic: 230.5 on 1 and 193 DF,  p-value: < 2.2e-16

c) Does the Price depend on the Impermeability? Shape?

Concerning Impermeability, the plots below show that there are some striking differences between the price distribution for Type 1 and Type 2, in particular observing the medians and the IQR.

Concerning Shapes, it is difficult to make any real conclusions regarding shape 3 and shape 4 given there are so few data points. We turn our attention to Shape 1 and Shape 2 - the IQR for these two shapes are seemingly different. This is confirmed by the result of the T Test.

impermability_plot_1 <- ggdotplot(raw_data,x="Impermeability",y="Price",color = "Impermeability", palette = "jco",binwidth = 1,legend="none")
shape_plot_1 <- ggdotplot(raw_data,x="Shape",y="Price",color = "Shape", palette = "npg",binwidth = 1,legend="none")
impermability_plot_2 <- ggboxplot(raw_data,x="Impermeability",y="Price",color = "Impermeability", palette = "jco",legend="none")
shape_plot_2 <- ggboxplot(raw_data,x="Shape",y="Price",color = "Shape", palette = "npg", legend = "none")
ggarrange(ggarrange(impermability_plot_1,impermability_plot_2,ncol = 2, nrow = 1),
           ggarrange(shape_plot_1,shape_plot_2,ncol = 2, nrow = 1),
           ncol = 1, nrow = 2)

summary(lm(Price~ Impermeability, data=raw_data))

Call:
lm(formula = Price ~ Impermeability, data = raw_data)

Residuals:
     Min       1Q   Median       3Q      Max 
-16.4106  -3.0187  -0.6286   2.4897  25.0638 

Coefficients:
                     Estimate Std. Error t value Pr(>|t|)    
(Intercept)           14.7236     0.4117   35.77   <2e-16 ***
ImpermeabilityType 2  14.5846     1.1986   12.17   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.399 on 193 degrees of freedom
Multiple R-squared:  0.4341,    Adjusted R-squared:  0.4312 
F-statistic:   148 on 1 and 193 DF,  p-value: < 2.2e-16
summary(lm(Price~ Shape, data=raw_data))

Call:
lm(formula = Price ~ Shape, data = raw_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-11.098  -3.850  -1.025   3.055  25.587 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   14.2006     0.5406  26.267  < 2e-16 ***
ShapeShape 2   8.1403     1.0782   7.550 1.75e-12 ***
ShapeShape 3   1.4510     2.2777   0.637  0.52485    
ShapeShape 4   7.4393     2.2777   3.266  0.00129 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.258 on 191 degrees of freedom
Multiple R-squared:  0.2475,    Adjusted R-squared:  0.2357 
F-statistic: 20.94 on 3 and 191 DF,  p-value: 9.008e-12

d) Which is the less expensive Supplier?

The answer to this question depends on the definition of expensive. We also note that there are few data points for Supplier C.

First, examine the following absolute metrics (this can be seen via the boxplot) 1) Absolute price - Supplier B cheapest (6.477451). However, Supplier B is also the supplier which has the highest absolute price (46.610372) 2) Average Price - Supplier C cheapest (14.88869)

Second, examine the following relative metrics:
3) Average Price / Unit Length - Supplier A (1.505043) 4) Average Price / Unit weight - Supplier A (9.013902) 5) Average Price / Unit Diameter - Supplier A (11.95632) 6) Average Price / Unit Mature.Volume - Supplier B (1.663305)

The result above suggest Supplier A has the cheapest average price per unit of production.

The analysis however is not complete given we do not have a definition of cheapest price. Even the scatter and box plots below suggest suppliers may cater to specific product ranges. We have not performed statistical tests to examine the significance of these differences. Categorical data could provide some insights into cheapest price for certain product features.

supplier_plot_1 <- ggboxplot(raw_data,x="Supplier",y="Price",color = "Supplier", palette = c("darkblue","red","darkgreen"),legend="none") + rotate()
supplier_plot_2 <- ggscatter(raw_data,x="Length",y="Price",color = "Supplier", palette = c("darkblue","red","darkgreen"),xscale= "log10", yscale="log10")
supplier_plot_3 <- ggscatter(raw_data,x="weight",y="Price",color = "Supplier", palette = c("darkblue","red","darkgreen"),xscale= "log10", yscale="log10")
supplier_plot_4 <- ggscatter(raw_data,x="Diameter",y="Price",color = "Supplier", palette = c("darkblue","red","darkgreen"),xscale= "log10", yscale="log10")
supplier_statistics <- raw_data %>% group_by(Supplier) %>%  summarise( "Average Price" = mean(Price), "Average Length" = mean(Length),"Average weight" = mean(weight),"Average Diameter" = mean(Diameter),"Average Mature.Volume" = mean(Mature.Volume)  ,"Average Price / Length" = mean(Price)/mean(Length), "Average Price / weight" = mean(Price)/mean(weight), "Average Price / Diameter" = mean(Price)/mean(Diameter),"Average Price / Mature.Volume" = mean(Price)/mean(Mature.Volume))
supplier_plot_1

supplier_plot_2

supplier_plot_3

supplier_plot_4

head(supplier_statistics)

3) One important point in explanatory data analysis consists in identifying potential outliers. Could you give points which are suspect regarding the Mature.Volume variable? Give the characteristics (other features) of the observations that seem suspsect

There are four data points which seem suspect - they have the same characteristics for Diameter, weight, nb.of.pieces, Impermeability, Finishing, Raw.Material and Mature.Volume. They differ in their supplier, price and length. These suggest some error in collating the data (system error / default data).

Mature.Volume_plot <- gghistogram(raw_data,x="Mature.Volume",y="..count..", color = "darkblue", fill = "lightgrey") + theme_minimal()
Using `bins = 30` by default. Pick better value with the argument `bins`.
Mature.Volume_plot

raw_data %>% filter (Mature.Volume > 6e+05 ) 

For the rest of the analysis, the 4 data points above are disregarded.

library(dplyr)
raw_data <- raw_data %>% filter (Mature.Volume < 6e+05 )

We will quickly check that there are no other noticeable outliers - this is indeed the case.

check_1 <- gghistogram(raw_data,x="Length",y="..count..", color = "darkblue", fill = "lightgrey") + theme_minimal()
Using `bins = 30` by default. Pick better value with the argument `bins`.
check_2 <- gghistogram(raw_data,x="Diameter",y="..count..", color = "darkblue", fill = "lightgrey") + theme_minimal()
Using `bins = 30` by default. Pick better value with the argument `bins`.
check_3 <- gghistogram(raw_data,x="weight",y="..count..", color = "darkblue", fill = "lightgrey") + theme_minimal()
Using `bins = 30` by default. Pick better value with the argument `bins`.
check_4 <- gghistogram(raw_data,x="nb.of.pieces",y="..count..", color = "darkblue", fill = "lightgrey",bins=40) + theme_minimal()
ggarrange(ggarrange(check_1,check_2,ncol=2,nrow=1),ggarrange(check_3,check_4,ncol=2,nrow=1),ncol=1,nrow=2)

4) Perform a PCA on the dataset ScrewCap, explain briefly what are the aims of a PCA and how categorical variables are handled?

Principal components analysis (PCA) is a technique for taking high-dimensional data, and using the dependencies between the variables to represent it in a more tractable, lower-dimensional form, without losing too much information - we try capture the essence of high dimentional data in a low dimensional representation. The aim of PCA is to draw conclusions from the linear relationships between variables by detecting the principal dimensions of variability. This may be for compression, denoising, data completion, anomaly detection or for preprocessing before supervised learning (improve performance / regularization to reduce overfitting).

The categorical variables cannot be represented in the same way as the supplementary quantitative variables since it is not possible to calculate the correlation between a categorical variable and the principal components. The categorical variables here are handled as supplemetary variables on a purely illustrative basis - they are not used to calculate the distance between inidividuals. We represent a categorical variable at the barycentre of all the individuals possessing that variable. A categorical variable on the PCA performed below can therefore be regarded as the mean individual obtained from the set of individuals who have it.

Given our ultimate goal here is to explore data prior to a multiple regression, it is advisable to choose the explanatory variables for the regression model as active variables for PCA, and to project the variable to be explained (the dependent variable) as a supplementary variable. This gives some idea of the relationships between explanatory variables and thus of the need to select explanatory variables. This also gives us an idea of the quality of the regression: if the dependent variable is appropriately projected, it will be a well-fitted model. Thus we select Price as a supplementary variable.

The dataset in this exercise contains 6 supplementary variables: - 1 quantitative variable (Price). - 5 qualitative variables (Supplier, Shape, Impermeability and Finishing).

res.pca <- PCA(raw_data,quali.sup = c(1,5,6,7,9),quanti.sup = 10, graph = FALSE,scale = TRUE)
fviz_pca_ind(res.pca, col.ind="cos2", label=c("quali"), geom = "point", title = "Individual factor map (PCA)", habillage = "none") + scale_color_gradient2(low="lightblue", mid="blue", high="darkblue", midpoint=0.6) + theme_minimal() 

plot.PCA(res.pca,choix = c("ind"),invisible = c("ind"))+theme_minimal()
NULL

plot.PCA(res.pca,choix = c("var"))+theme_minimal()
NULL

5) Compute the correlation matrix between the variables and comment it with respect to the correlation circle

The first task is to center and standardize the variables. Then the correlation matrix is computed. All variable vectors are quite near to the boundary of the correlation circle on the variables plot - thus the variables are relatively well projected on the 2 dimensional subspace. We now turn our attention to correlations between variables. The correlations can be visualised through the angles between variables on the correlation matrix. This can be related to the correlation matrix (small angles suggest large positive correlation, 90 degree angles suggest no correlation, 180 degree angles suggest large negative correlation). - Diameter, Length and weight expose very strong corrleation: the angle between them is close to 0, suggesting correlation close to 1. These are all very highly correlated to the first Principal Component. - The three variables above are at an angle sightly wider than a right angle to both nb.of.pieces and Mature.Volume in the cirlce which suggests slightly negative correlation. - Price is highlighly correlatd to the three variables above - Equally, Mature.Volume and nb.of.pieces are at a slightly wider angle than a right angle which suggests slightly negative correlation - this suggests that when the screw caps have a high number of pieces, the company orders a smaller volume of these. These are well projected on the second principal component.

don <- as.matrix(raw_data[,-c(1,5,6,7,9,10)]) %>% scale()
don_correlation <- cor(don)
don_correlation
                Diameter     weight nb.of.pieces Mature.Volume     Length
Diameter       1.0000000  0.9622544  -0.14869500   -0.29164724  0.9996963
weight         0.9622544  1.0000000  -0.16884367   -0.31321323  0.9627460
nb.of.pieces  -0.1486950 -0.1688437   1.00000000   -0.07462463 -0.1463770
Mature.Volume -0.2916472 -0.3132132  -0.07462463    1.00000000 -0.2936330
Length         0.9996963  0.9627460  -0.14637705   -0.29363295  1.0000000
plot.PCA(res.pca,choix = c("var"))+theme_minimal()
NULL

6) On what kind of relationship PCA focuses? Is it a problem?

PCA focuses on the linear relationships between continuous variables. Given complex links also exist, such as quadratic relationships, logarithmics, exponential functions, and so forth, this may seem restrictive, but in practice many relationships can be considered linear, at least for an initial approximation. However, there is obviously non-linear datasets for which PCA will have pitfalls (e.g. spiral dataset). Furthermore, in PCA categorical variables cannot be active variables, which can be restrictive.

7) Comment the PCA outputs

Comment the position of the categories Impermeability=type 2 and Raw.Material=PS.

The coordinates for Type 2 are (3.28 , 0.01) for the first two principal components. It has a very significant p value for Dim 1 and thus the coordinate is significantly different from 0 on the first component. The coordinate for PS are (2.67, -0.25) for the first two principal components. It has a very significant p value for Dim 1 and thus he coordinate is significantly different from 0 on the first component.

Furthermore, given the correlation circle shows high correlation between the first component and price, diameter, length and weight, this suggest Type 2 and PS have high values for these variables.

In fact, looking at the p-values we can say that both of the categories Type 1, Type 2, PS and PP have coordinates that are significantly different from 0 on the first component. As the value is positive (negative) for the Type 2 / PS (Type 1 / PP) we can say that the rows which include Type 2 / PS tended to have positive coordinates (or negative, respectively) on component 1 and thus are more correlated with the variables mentioned above (price,diameter, length and weight).

Finally, we also consider the results from the wilk’s test performed in the FactoInvestigate package. This indicates which variables are the best seperated on the plane (i,e, which one explain the best the distance between individuals, and the best qualitative variables to illutrate distance between individuals on the plane are Impermeability and Raw.Material.

res.pca$quali.sup$coord
                   Dim.1        Dim.2        Dim.3         Dim.4         Dim.5
Supplier A    0.54805992 -0.054566515 -0.214051234  0.0227636641  0.0058684306
Supplier B   -0.06543165 -0.125589918 -0.026949041  0.0018781980 -0.0006195266
Supplier C   -0.44356100  1.440695488  0.728281700 -0.0670085402 -0.0056067533
Shape 1      -0.42564773 -0.137916238 -0.214123559  0.0065253174 -0.0009308749
Shape 2       1.42726960  0.394279456  0.383010989 -0.0314353290  0.0018793388
Shape 3      -0.55969671 -0.332207048  0.059604995  0.1360698967 -0.0029996610
Shape 4      -0.55191919  0.355523978  1.265466019 -0.0652825793  0.0075550964
Type 1       -0.45031131 -0.001823621 -0.009194259  0.0008200692 -0.0005392760
Type 2        3.28923043  0.013320364  0.067158065 -0.0059900708  0.0039390597
Hot Printing -0.28600503 -0.037712714  0.192161713  0.0717729126 -0.0006010224
Lacquering    0.13745978  0.018125491 -0.092356792 -0.0344955084  0.0002888635
ABS           0.87599666  0.220028373 -0.581512708  0.0043591307 -0.0032513149
PP           -0.61062316  0.013651457  0.120658551  0.0042947466 -0.0005390562
PS            2.67437709 -0.253323291 -0.198579404 -0.0273071254  0.0056116038
dimdesc(res.pca, 1:2)
$Dim.1
$Dim.1$quanti
              correlation       p.value
Length          0.9853764 3.259183e-147
Diameter        0.9851090 1.784008e-146
weight          0.9774643 1.263294e-129
Price           0.7960132  4.472456e-43
nb.of.pieces   -0.2017085  5.139018e-03
Mature.Volume  -0.4118157  3.243173e-09

$Dim.1$quali
                      R2      p.value
Impermeability 0.4767041 2.203784e-28
Raw.Material   0.4309747 9.602186e-24
Shape          0.2024825 3.268025e-09

$Dim.1$category
          Estimate      p.value
Type 2   1.8697709 2.203784e-28
PS       1.6944602 3.078822e-20
Shape 2  1.4547681 6.874053e-11
ABS     -0.1039202 1.566216e-02
Shape 1 -0.3981492 5.692581e-07
PP      -1.5905400 1.465743e-20
Type 1  -1.8697709 2.203784e-28


$Dim.2
$Dim.2$quanti
              correlation      p.value
nb.of.pieces    0.8426737 1.045066e-52
Price           0.1706662 1.824955e-02
Mature.Volume  -0.5956751 9.962493e-20

$Dim.2$quali
                 R2      p.value
Supplier 0.15447684 1.411880e-07
Shape    0.05575798 1.311396e-02

$Dim.2$category
             Estimate      p.value
Supplier C  1.0205158 1.996715e-08
Shape 2     0.3243594 3.249853e-03
Shape 1    -0.2078363 6.889304e-03
Supplier B -0.5457696 1.703585e-03
wilks.p <-structure(c(3.63471614445021e-27, 1.29050347661083e-22, 4.08870696535012e-10, 
3.73163828614179e-07, 0.284227353624445), .Names = c("Impermeability", 
"Raw.Material", "Shape", "Supplier", "Finishing"))
wilks.p
Impermeability   Raw.Material          Shape       Supplier      Finishing 
  3.634716e-27   1.290503e-22   4.088707e-10   3.731638e-07   2.842274e-01 

Comment the percentage of inertia

Below in the Scree plot we see the percentage of inertia explained by each component. The first two components explain 83.48% of the total dataset inertial - this means that 83.50% of the individuals (or variables) cloud total variability is explained by this plane. Over 95% of the variance can be explained with the 3 first synthetic vectors in the PCA.

We can also see that the variance of the first component is explained in majority by Diameter, Length and weight as expected. In the second dimension by nb.of.pieces and Mature.Volume.

res.pca$eig
         eigenvalue percentage of variance cumulative percentage of variance
comp 1 3.1071215080           62.142430160                          62.14243
comp 2 1.0669070766           21.338141532                          83.48057
comp 3 0.7768681861           15.537363723                          99.01794
comp 4 0.0488056018            0.976112036                          99.99405
comp 5 0.0002976274            0.005952549                         100.00000
fviz_eig(res.pca, addlabels = TRUE)

res.pca$var$contrib
                  Dim.1       Dim.2     Dim.3       Dim.4        Dim.5
Diameter      31.232761  0.06080408  2.673943 16.37238500 4.966011e+01
weight        30.749891  0.07707453  1.371044 67.79961131 2.379254e-03
nb.of.pieces   1.309454 66.55678435 32.075772  0.05771098 2.791480e-04
Mature.Volume  5.458176 33.25770535 61.213010  0.07097644 1.329131e-04
Length        31.249719  0.04763169  2.666232 15.69931627 5.033710e+01

8) Give the R object with the two principal components which are the synthetic variables the most correlated to all the variables.

These are found in the code below -

res.pca$var$coord[,1:2]
                   Dim.1       Dim.2
Diameter       0.9851090 -0.02547004
weight         0.9774643 -0.02867601
nb.of.pieces  -0.2017085  0.84267375
Mature.Volume -0.4118157 -0.59567509
Length         0.9853764 -0.02254298

9) PCA is often used as a pre-processing step before applying a clustering algorithm, explain the rationale of this approach and how many components k you keep.

We chose the maximum number of components as to not to lose any significant information whilst discarding the components that can be considered as noise. Consequently, we keep the number of dimensions such that we keep 95% of the inertia in PCA, which is equivalent to 3 components in our analysis.

10) Perfoms a kmeans algorithm on the selected k principal components of PCA. How many cluster are you keeping? Justify.

We us the Elbow method and look at the knee to determine the number of clusters we keep 3 clusters here.

Recall that, the basic idea behind k-means clustering is to define clusters such that the total within-cluster variation is minimized. The total WSS measures the compactness of the clustering and we want it to be as small as possible. The Elbow method looks at the total WSS as a function of the number of clusters: one should choose a number of clusters so that the marginal cluster doesn’t improve WSS. Here this value is 3.

#Perform PCA on 3 Principal Components
res.pca_3 <- PCA(raw_data,quali.sup = c(1,5,6,7,9),quanti.sup = 10, ncp=3, graph = FALSE, scale = TRUE) 
#Use the Elbow method to determine the number of clusters to keep
fviz_nbclust(res.pca_3$ind$coord, kmeans, method = "wss") + geom_vline(xintercept = 3, linetype = 2)

#Kmeans Algorithm 
k_cluster <- kmeans(res.pca_3$ind$coord, 3)
k_mean_data <- as.matrix(res.pca_3$ind$coord)
plot(k_mean_data, col = k_cluster$cluster, pch = 20, frame = FALSE,  main = "K-means")

11) Perform an AHC on the selected k principal components of PCA.

Below we perform an AHC on the select 3 principal components of PCA.

res.pca_3 <- PCA(raw_data,quali.sup = c(1,5,6,7,9),quanti.sup = 10, ncp=3, graph = FALSE, scale = TRUE) 
res.hcpc <- HCPC(res.pca_3, nb.clust = -1, graph = FALSE)
Chi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrect
plot.HCPC(res.hcpc, choice = 'map', draw.tree = FALSE, select = "cos2 5", title = 'Factor Map')

plot.HCPC(res.hcpc, choice = 'bar', draw.tree = FALSE, title = 'Inertia Gain')

plot.HCPC(res.hcpc, choice = '3D.map', draw.tree = FALSE, title = 'Hierarchical Clustering on the Factor Map', angle=60)

plot.HCPC(res.hcpc, choice = 'tree', draw.tree = FALSE, title = 'Hierarchical Clustering')

12) Comments the results and describe precisely one cluster.

The classfication made on individuals reveals 3 clusters.

We now aim at describing the clusters.

For each quantitative variable, we build an analysis of variance model between the quantitative variable and the class variable, do a Fisher test to detect class effect and sort the variables by increasing p-value. We can directly find the results in the following objects:

res.hcpc$desc.var$quanti.var
                   Eta2      P-value
Length        0.8036361 3.530117e-67
Diameter      0.8025769 5.853470e-67
weight        0.8013378 1.053993e-66
Mature.Volume 0.7588382 8.649758e-59
Price         0.4812030 1.620918e-27
nb.of.pieces  0.1760507 1.243497e-08

For high values of Eta2, as seen in the lecture, there is a relation between the clustering and the quantitative variable. Thus we can say this is the case for Length, Diameter, weight and Mature.Volume.

We now turn our attention to describing the observations in a cluster using quantitative variables.

res.hcpc$desc.var$quanti
$`1`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
Mature.Volume 11.942982     2.431183e+05 82206.026178   67166.762125 9.103190e+04 7.064414e-33
Diameter      -3.255425     8.214269e-01     1.294639       0.254233 9.821218e-01 1.132228e-03
Length        -3.297003     6.491733e+00    10.329589       2.056760 7.864783e+00 9.772253e-04
weight        -3.536244     1.100262e+00     1.714121       0.315574 1.172854e+00 4.058595e-04
nb.of.pieces  -3.780986     3.324324e+00     4.115183       1.274576 1.413225e+00 1.562083e-04
Price         -3.857939     1.245686e+01    16.552332       4.115901 7.172431e+00 1.143473e-04

$`2`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
nb.of.pieces   5.739229         4.503759     4.115183   1.352381e+00 1.413225e+00 9.510856e-09
Price         -2.999298        15.521715    16.552332   4.620374e+00 7.172431e+00 2.706026e-03
weight        -5.297059         1.416482     1.714121   3.882302e-01 1.172854e+00 1.176825e-07
Length        -5.533133         8.244766    10.329589   2.492726e+00 7.864783e+00 3.145605e-08
Diameter      -5.565997         1.032748     1.294639   3.121379e-01 9.821218e-01 2.606582e-08
Mature.Volume -8.031930     47177.255639 82206.026178   3.971314e+04 9.103190e+04 9.595124e-16

$`3`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
Length        12.298789        30.295407    10.329589   7.979008e+00 7.864783e+00 9.194180e-35
Diameter      12.294570         3.787033     1.294639   1.000521e+00 9.821218e-01 9.687099e-35
weight        12.254018         4.680731     1.714121   1.164251e+00 1.172854e+00 1.598746e-34
Price          9.282815        30.295414    16.552332   8.814239e+00 7.172431e+00 1.650583e-20
Mature.Volume -3.281665     20542.857143 82206.026178   1.547128e+04 9.103190e+04 1.031962e-03
nb.of.pieces  -3.659694         3.047619     4.115183   7.221786e-01 1.413225e+00 2.525166e-04

Below, we also illustrate clusters by considering paragons and specific indivuals.

res.hcpc$desc.ind
$para
Cluster: 1
       94        95       142        74       144 
0.3280604 0.3376965 0.4205536 0.4265863 0.5886435 
--------------------------------------------------------------------------------------------------------------------------------- 
Cluster: 2
       82       149       129       148        36 
0.3560908 0.3691347 0.3744143 0.3849945 0.3990805 
--------------------------------------------------------------------------------------------------------------------------------- 
Cluster: 3
        2         3       157         1       160 
0.4151628 0.4690793 0.5302929 0.9668707 1.0514049 

$dist
Cluster: 1
      77      104      105      102      146 
4.439104 4.427938 3.643115 3.369384 3.000193 
--------------------------------------------------------------------------------------------------------------------------------- 
Cluster: 2
     183       45      178      191      188 
4.955342 3.980645 3.691443 3.447450 3.447219 
--------------------------------------------------------------------------------------------------------------------------------- 
Cluster: 3
      85       84      159      156      158 
7.892354 7.761370 7.666985 6.749803 6.732176 

Finally, we also look at the cateegorical variables for clues on clusters:

res.hcpc$desc.var$test.chi2
                    p.value df
Impermeability 5.318642e-18  2
Raw.Material   5.226547e-17  4
Shape          5.626207e-06  6
Supplier       4.102258e-02  4

The variable Impermeability and Raw.Materials seem to be very siginificatively linked to partioning, with Shape and Supplier also significant to a certaint extent. For each qualitative variable, we perform a chi2 test between it and the class variable and then sort the variables by increasing p-value.

res.hcpc$desc.var$category
$`1`
                        Cla/Mod    Mod/Cla    Global      p.value    v.test
Raw.Material=PP       25.000000  97.297297 75.392670 0.0001368886  3.813724
Impermeability=Type 1 22.023810 100.000000 87.958115 0.0049829303  2.808135
Supplier=Supplier C    0.000000   0.000000  7.329843 0.0434829294 -2.019041
Raw.Material=PS        3.846154   2.702703 13.612565 0.0222292828 -2.286427
Raw.Material=ABS       0.000000   0.000000 10.994764 0.0081544536 -2.645607
Impermeability=Type 2  0.000000   0.000000 12.041885 0.0049829303 -2.808135
Shape=Shape 2          2.222222   2.702703 23.560209 0.0002330416 -3.680210

$`2`
                        Cla/Mod   Mod/Cla    Global      p.value    v.test
Impermeability=Type 1  74.40476 93.984962 87.958115 0.0002868332  3.626910
Supplier=Supplier C   100.00000 10.526316  7.329843 0.0050545222  2.803538
Raw.Material=PP        74.30556 80.451128 75.392670 0.0173789787  2.378590
Raw.Material=PS        38.46154  7.518797 13.612565 0.0004703729 -3.497084
Impermeability=Type 2  34.78261  6.015038 12.041885 0.0002868332 -3.626910

$`3`
                         Cla/Mod   Mod/Cla   Global      p.value    v.test
Impermeability=Type 2 65.2173913 71.428571 12.04188 2.909966e-12  6.982005
Raw.Material=PS       57.6923077 71.428571 13.61257 4.169068e-11  6.597941
Shape=Shape 2         31.1111111 66.666667 23.56021 9.932485e-06  4.418638
Shape=Shape 1          5.3846154 33.333333 68.06283 6.715709e-04 -3.400930
Impermeability=Type 1  3.5714286 28.571429 87.95812 2.909966e-12 -6.982005
Raw.Material=PP        0.6944444  4.761905 75.39267 2.869539e-13 -7.300381

We can confirm that the these qualitative variables play a significant role in each of the clusters individually as well.

We link this all back to our PCA -

res.hcpc$desc.axes
$quanti.var
           Eta2      P-value
Dim.1 0.8309664 2.688258e-73
Dim.2 0.5350712 5.424124e-32
Dim.3 0.2440799 3.773433e-12

$quanti
$quanti$`1`
         v.test Mean in category  Overall mean sd in category Overall sd      p.value
Dim.3  6.487292        0.8462912  1.421520e-15      0.8757617  0.8814013 8.739298e-11
Dim.1 -4.527644       -1.1812290 -5.988883e-16      0.4424802  1.7627029 5.964496e-06
Dim.2 -9.411906       -1.4388767  1.737821e-15      0.7723735  1.0329119 4.872192e-21

$quanti$`2`
         v.test Mean in category  Overall mean sd in category Overall sd      p.value
Dim.2  9.409551        0.4656328  1.737821e-15      0.7182786  1.0329119 4.982628e-21
Dim.1 -4.493869       -0.3794992 -5.988883e-16      0.5207778  1.7627029 6.994079e-06
Dim.3 -6.203223       -0.2619404  1.421520e-15      0.7584515  0.8814013 5.531849e-10

$quanti$`3`
        v.test Mean in category  Overall mean sd in category Overall sd      p.value
Dim.1 12.32585         4.484708 -5.988883e-16       1.647504   1.762703 6.574308e-35


attr(,"class")
[1] "catdes" "list " 

The information above suggests there is strong relationships between the clusters and the dimensions: cluster 1 - have coordinates signicantly smaller to 0 in the first dimension and second dimension but coordinates signficantly greater than 0 in the third dimension. cluster 2 - have coordinates signicantly smaller to 0 in the first dimension and third dimension but coordinates signficantly greater than 0 in the second dimension. cluster 3 - have coordinates signicantly greater to 0 in the first dimension.

This is extremely insightful when we consider the analysis we have performed previously.

In english, the summary of the above analysis can be written as:

The cluster 1 is made of individuals such as specific individual 77 sharing : - high values for the variable Mature.Volume. - low values for the variables nb.of.pieces, Price, weight, Length and Diameter (variables are sorted from the weakest).

The cluster 2 is made of individuals sharing such as specific individual 183 sharing: - high values for the variable nb.of.pieces. - low values for the variables Mature.Volume, Diameter, Length, weight and Price (variables are sorted from the weakest).

The cluster 3 is made of individuals such as specific individual 84 sharing : - high values for the variables Length, Diameter, weight and Price (variables are sorted from the strongest). - low values for the variables nb.of.pieces and Mature.Volume (variables are sorted from the weakest).

13) If someone ask you why you have selected k components and not k + 1 or k − 1, what is your answer? (could you suggest a strategy to assess the stability of the approach? - are there many differences between the clustering obtained on k components or on the initial data)

In the Husson textbook, it is suggested to test the percentage intertia expressed by a component and then the percentage of inertia expressed by the first plane. The method simulates 10,000 datasets for 191 individual sand 11 normally distributed independent variables - this allow us to make a comparison by taking into account the number of active individuals and active variables. It then conduct a standardised PCA for each dataset and calculates the percentage of inertia explained by one component and the that expressed by one plane. Then, the method defines the quantile 0.95 of the 10,000 percentages of inertia of the first component (and the first plane, respectively) obtained. Comparing the percentage of inertia of a component or plane with the associated value in the table then amounts to testing the null hypothesis H0: the percentage of inertia explained by the first component (and the first plane, respectively) is not significantly greater than that obtained with the (normally distributed) independent data.

The first two dimensions of PCA express 83.48% of the total dataset inertia - this value is strongly greater than the reference value that equals 48.22% using the methodology above. This would suggest it is probably not useful to interpret the next dimensions and would be the basis of k+1, k-1 decisions.

However, this method might discard significant information. Consequently, we will use a methodology similar to the Elbow method - we perform HCPC with k-1, k and k+1 components and compare and contrast the results. For example, we could look at the quality and significance gain that each PCA offers when anaylysing the clusters. Here this corresponds to 2,3 and 4 components. We notice that for all 3, HCPC will cut the tree at 3 clusters (visually the three plots are similar). However, after analysing the p-values we notice that there is an improvement from going to 2 to 3 whereas from p-values for 3 and 4 are very similar.

res.pca <- PCA(raw_data,quali.sup = c(1,5,6,7,9),quanti.sup = 10,ncp=4, scale=TRUE, graph = FALSE)
res.hcpc <- HCPC(res.pca, nb.clust = -1, graph = FALSE)
Chi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrect
res.hcpc$desc.var
$test.chi2
                    p.value df
Impermeability 5.318642e-18  2
Raw.Material   5.226547e-17  4
Shape          5.626207e-06  6
Supplier       4.102258e-02  4

$category
$category$`1`
                        Cla/Mod    Mod/Cla    Global      p.value    v.test
Raw.Material=PP       25.000000  97.297297 75.392670 0.0001368886  3.813724
Impermeability=Type 1 22.023810 100.000000 87.958115 0.0049829303  2.808135
Supplier=Supplier C    0.000000   0.000000  7.329843 0.0434829294 -2.019041
Raw.Material=PS        3.846154   2.702703 13.612565 0.0222292828 -2.286427
Raw.Material=ABS       0.000000   0.000000 10.994764 0.0081544536 -2.645607
Impermeability=Type 2  0.000000   0.000000 12.041885 0.0049829303 -2.808135
Shape=Shape 2          2.222222   2.702703 23.560209 0.0002330416 -3.680210

$category$`2`
                        Cla/Mod   Mod/Cla    Global      p.value    v.test
Impermeability=Type 1  74.40476 93.984962 87.958115 0.0002868332  3.626910
Supplier=Supplier C   100.00000 10.526316  7.329843 0.0050545222  2.803538
Raw.Material=PP        74.30556 80.451128 75.392670 0.0173789787  2.378590
Raw.Material=PS        38.46154  7.518797 13.612565 0.0004703729 -3.497084
Impermeability=Type 2  34.78261  6.015038 12.041885 0.0002868332 -3.626910

$category$`3`
                         Cla/Mod   Mod/Cla   Global      p.value    v.test
Impermeability=Type 2 65.2173913 71.428571 12.04188 2.909966e-12  6.982005
Raw.Material=PS       57.6923077 71.428571 13.61257 4.169068e-11  6.597941
Shape=Shape 2         31.1111111 66.666667 23.56021 9.932485e-06  4.418638
Shape=Shape 1          5.3846154 33.333333 68.06283 6.715709e-04 -3.400930
Impermeability=Type 1  3.5714286 28.571429 87.95812 2.909966e-12 -6.982005
Raw.Material=PP        0.6944444  4.761905 75.39267 2.869539e-13 -7.300381


$quanti.var
                   Eta2      P-value
Length        0.8036361 3.530117e-67
Diameter      0.8025769 5.853470e-67
weight        0.8013378 1.053993e-66
Mature.Volume 0.7588382 8.649758e-59
Price         0.4812030 1.620918e-27
nb.of.pieces  0.1760507 1.243497e-08

$quanti
$quanti$`1`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
Mature.Volume 11.942982     2.431183e+05 82206.026178   67166.762125 9.103190e+04 7.064414e-33
Diameter      -3.255425     8.214269e-01     1.294639       0.254233 9.821218e-01 1.132228e-03
Length        -3.297003     6.491733e+00    10.329589       2.056760 7.864783e+00 9.772253e-04
weight        -3.536244     1.100262e+00     1.714121       0.315574 1.172854e+00 4.058595e-04
nb.of.pieces  -3.780986     3.324324e+00     4.115183       1.274576 1.413225e+00 1.562083e-04
Price         -3.857939     1.245686e+01    16.552332       4.115901 7.172431e+00 1.143473e-04

$quanti$`2`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
nb.of.pieces   5.739229         4.503759     4.115183   1.352381e+00 1.413225e+00 9.510856e-09
Price         -2.999298        15.521715    16.552332   4.620374e+00 7.172431e+00 2.706026e-03
weight        -5.297059         1.416482     1.714121   3.882302e-01 1.172854e+00 1.176825e-07
Length        -5.533133         8.244766    10.329589   2.492726e+00 7.864783e+00 3.145605e-08
Diameter      -5.565997         1.032748     1.294639   3.121379e-01 9.821218e-01 2.606582e-08
Mature.Volume -8.031930     47177.255639 82206.026178   3.971314e+04 9.103190e+04 9.595124e-16

$quanti$`3`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
Length        12.298789        30.295407    10.329589   7.979008e+00 7.864783e+00 9.194180e-35
Diameter      12.294570         3.787033     1.294639   1.000521e+00 9.821218e-01 9.687099e-35
weight        12.254018         4.680731     1.714121   1.164251e+00 1.172854e+00 1.598746e-34
Price          9.282815        30.295414    16.552332   8.814239e+00 7.172431e+00 1.650583e-20
Mature.Volume -3.281665     20542.857143 82206.026178   1.547128e+04 9.103190e+04 1.031962e-03
nb.of.pieces  -3.659694         3.047619     4.115183   7.221786e-01 1.413225e+00 2.525166e-04


attr(,"class")
[1] "catdes" "list " 
plot.HCPC(res.hcpc, choice = 'map', draw.tree = FALSE, select = "cos2 5", title = 'Factor Map k=4')

res.pca <- PCA(raw_data,quali.sup = c(1,5,6,7,9),quanti.sup = 10, ncp=3, scale = TRUE, graph = FALSE)
res.hcpc <- HCPC(res.pca, nb.clust = -1,graph = FALSE)
Chi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrect
res.hcpc$desc.var
$test.chi2
                    p.value df
Impermeability 5.318642e-18  2
Raw.Material   5.226547e-17  4
Shape          5.626207e-06  6
Supplier       4.102258e-02  4

$category
$category$`1`
                        Cla/Mod    Mod/Cla    Global      p.value    v.test
Raw.Material=PP       25.000000  97.297297 75.392670 0.0001368886  3.813724
Impermeability=Type 1 22.023810 100.000000 87.958115 0.0049829303  2.808135
Supplier=Supplier C    0.000000   0.000000  7.329843 0.0434829294 -2.019041
Raw.Material=PS        3.846154   2.702703 13.612565 0.0222292828 -2.286427
Raw.Material=ABS       0.000000   0.000000 10.994764 0.0081544536 -2.645607
Impermeability=Type 2  0.000000   0.000000 12.041885 0.0049829303 -2.808135
Shape=Shape 2          2.222222   2.702703 23.560209 0.0002330416 -3.680210

$category$`2`
                        Cla/Mod   Mod/Cla    Global      p.value    v.test
Impermeability=Type 1  74.40476 93.984962 87.958115 0.0002868332  3.626910
Supplier=Supplier C   100.00000 10.526316  7.329843 0.0050545222  2.803538
Raw.Material=PP        74.30556 80.451128 75.392670 0.0173789787  2.378590
Raw.Material=PS        38.46154  7.518797 13.612565 0.0004703729 -3.497084
Impermeability=Type 2  34.78261  6.015038 12.041885 0.0002868332 -3.626910

$category$`3`
                         Cla/Mod   Mod/Cla   Global      p.value    v.test
Impermeability=Type 2 65.2173913 71.428571 12.04188 2.909966e-12  6.982005
Raw.Material=PS       57.6923077 71.428571 13.61257 4.169068e-11  6.597941
Shape=Shape 2         31.1111111 66.666667 23.56021 9.932485e-06  4.418638
Shape=Shape 1          5.3846154 33.333333 68.06283 6.715709e-04 -3.400930
Impermeability=Type 1  3.5714286 28.571429 87.95812 2.909966e-12 -6.982005
Raw.Material=PP        0.6944444  4.761905 75.39267 2.869539e-13 -7.300381


$quanti.var
                   Eta2      P-value
Length        0.8036361 3.530117e-67
Diameter      0.8025769 5.853470e-67
weight        0.8013378 1.053993e-66
Mature.Volume 0.7588382 8.649758e-59
Price         0.4812030 1.620918e-27
nb.of.pieces  0.1760507 1.243497e-08

$quanti
$quanti$`1`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
Mature.Volume 11.942982     2.431183e+05 82206.026178   67166.762125 9.103190e+04 7.064414e-33
Diameter      -3.255425     8.214269e-01     1.294639       0.254233 9.821218e-01 1.132228e-03
Length        -3.297003     6.491733e+00    10.329589       2.056760 7.864783e+00 9.772253e-04
weight        -3.536244     1.100262e+00     1.714121       0.315574 1.172854e+00 4.058595e-04
nb.of.pieces  -3.780986     3.324324e+00     4.115183       1.274576 1.413225e+00 1.562083e-04
Price         -3.857939     1.245686e+01    16.552332       4.115901 7.172431e+00 1.143473e-04

$quanti$`2`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
nb.of.pieces   5.739229         4.503759     4.115183   1.352381e+00 1.413225e+00 9.510856e-09
Price         -2.999298        15.521715    16.552332   4.620374e+00 7.172431e+00 2.706026e-03
weight        -5.297059         1.416482     1.714121   3.882302e-01 1.172854e+00 1.176825e-07
Length        -5.533133         8.244766    10.329589   2.492726e+00 7.864783e+00 3.145605e-08
Diameter      -5.565997         1.032748     1.294639   3.121379e-01 9.821218e-01 2.606582e-08
Mature.Volume -8.031930     47177.255639 82206.026178   3.971314e+04 9.103190e+04 9.595124e-16

$quanti$`3`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
Length        12.298789        30.295407    10.329589   7.979008e+00 7.864783e+00 9.194180e-35
Diameter      12.294570         3.787033     1.294639   1.000521e+00 9.821218e-01 9.687099e-35
weight        12.254018         4.680731     1.714121   1.164251e+00 1.172854e+00 1.598746e-34
Price          9.282815        30.295414    16.552332   8.814239e+00 7.172431e+00 1.650583e-20
Mature.Volume -3.281665     20542.857143 82206.026178   1.547128e+04 9.103190e+04 1.031962e-03
nb.of.pieces  -3.659694         3.047619     4.115183   7.221786e-01 1.413225e+00 2.525166e-04


attr(,"class")
[1] "catdes" "list " 
plot.HCPC(res.hcpc, choice = 'map', draw.tree = FALSE, select = "cos2 5", title = 'Factor Map k=3')

res.pca <- PCA(raw_data,quali.sup = c(1,5,6,7,9),quanti.sup = 10,ncp=2, scale = TRUE,graph = FALSE)
res.hcpc <- HCPC(res.pca, nb.clust = -1, graph = FALSE)
Chi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrect
res.hcpc$desc.var
$test.chi2
                    p.value df
Impermeability 5.047015e-18  2
Raw.Material   1.194071e-16  4
Shape          3.963181e-06  6
Supplier       3.906405e-02  4

$category
$category$`1`
                        Cla/Mod Mod/Cla    Global      p.value    v.test
Raw.Material=PP       26.388889    95.0 75.392670 4.760733e-04  3.493870
Impermeability=Type 1 23.809524   100.0 87.958115 3.056564e-03  2.961991
Raw.Material=ABS       4.761905     2.5 10.994764 4.405004e-02 -2.013614
Supplier=Supplier C    0.000000     0.0  7.329843 3.260506e-02 -2.136913
Raw.Material=PS        3.846154     2.5 13.612565 1.378402e-02 -2.462843
Impermeability=Type 2  0.000000     0.0 12.041885 3.056564e-03 -2.961991
Shape=Shape 2          2.222222     2.5 23.560209 9.002617e-05 -3.916011

$category$`2`
                        Cla/Mod   Mod/Cla    Global      p.value    v.test
Impermeability=Type 1  72.61905 93.846154 87.958115 0.0005815138  3.440093
Supplier=Supplier C   100.00000 10.769231  7.329843 0.0036107554  2.910306
Raw.Material=PP        72.91667 80.769231 75.392670 0.0146197613  2.441664
Raw.Material=PS        38.46154  7.692308 13.612565 0.0009776513 -3.296880
Impermeability=Type 2  34.78261  6.153846 12.041885 0.0005815138 -3.440093

$category$`3`
                         Cla/Mod   Mod/Cla   Global      p.value    v.test
Impermeability=Type 2 65.2173913 71.428571 12.04188 2.909966e-12  6.982005
Raw.Material=PS       57.6923077 71.428571 13.61257 4.169068e-11  6.597941
Shape=Shape 2         31.1111111 66.666667 23.56021 9.932485e-06  4.418638
Shape=Shape 1          5.3846154 33.333333 68.06283 6.715709e-04 -3.400930
Impermeability=Type 1  3.5714286 28.571429 87.95812 2.909966e-12 -6.982005
Raw.Material=PP        0.6944444  4.761905 75.39267 2.869539e-13 -7.300381


$quanti.var
                   Eta2      P-value
Length        0.8077126 4.913463e-68
Diameter      0.8068180 7.600787e-68
weight        0.8049086 1.915936e-67
Mature.Volume 0.5465422 5.181644e-33
Price         0.5051780 1.897610e-29
nb.of.pieces  0.3011815 2.345580e-15

$quanti
$quanti$`1`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
Mature.Volume 10.081948     2.115719e+05 82206.026178   9.227311e+04 9.103190e+04 6.639678e-24
Diameter      -3.662423     7.876300e-01     1.294639   2.280777e-01 9.821218e-01 2.498407e-04
Length        -3.685271     6.244158e+00    10.329589   1.857052e+00 7.864783e+00 2.284591e-04
weight        -3.854063     1.076967e+00     1.714121   3.205977e-01 1.172854e+00 1.161737e-04
Price         -4.760131     1.173987e+01    16.552332   3.990345e+00 7.172431e+00 1.934675e-06
nb.of.pieces  -5.849266     2.950000e+00     4.115183   8.645808e-01 1.413225e+00 4.937483e-09

$quanti$`2`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
nb.of.pieces   7.560366         4.646154     4.115183   1.317699e+00 1.413225e+00 4.019369e-14
Price         -2.074083        15.813052    16.552332   4.466612e+00 7.172431e+00 3.807163e-02
weight        -4.858401         1.430947     1.714121   3.790811e-01 1.172854e+00 1.183376e-06
Length        -5.035751         8.361397    10.329589   2.475781e+00 7.864783e+00 4.759789e-07
Diameter      -5.052860         1.048024     1.294639   3.099323e-01 9.821218e-01 4.352422e-07
Mature.Volume -6.596988     52362.115385 82206.026178   5.350229e+04 9.103190e+04 4.195953e-11

$quanti$`3`
                 v.test Mean in category Overall mean sd in category   Overall sd      p.value
Length        12.298789        30.295407    10.329589   7.979008e+00 7.864783e+00 9.194180e-35
Diameter      12.294570         3.787033     1.294639   1.000521e+00 9.821218e-01 9.687099e-35
weight        12.254018         4.680731     1.714121   1.164251e+00 1.172854e+00 1.598746e-34
Price          9.282815        30.295414    16.552332   8.814239e+00 7.172431e+00 1.650583e-20
Mature.Volume -3.281665     20542.857143 82206.026178   1.547128e+04 9.103190e+04 1.031962e-03
nb.of.pieces  -3.659694         3.047619     4.115183   7.221786e-01 1.413225e+00 2.525166e-04


attr(,"class")
[1] "catdes" "list " 
plot.HCPC(res.hcpc, choice = 'map', draw.tree = FALSE, select = "cos2 5", title = 'Factor Map k=2')

14) The methodology that you have used to describe clusters can also be used to describe a categorical variable, for instance the supplier. Use the function catdes(data, num.var=1) and explain how this information can be useful for the company.

From the chi squared tests, we have an indication that Raw.Material and Impermeability have statistical significance with regard to the different suppliers groups. In particular, we have the following characteristics for each supplier (from most significant to least significant): Supplier A: PS, Type 2, Type 1, Shape 2 and ABS Supplier B: Shape 2, ABS, PS ; nb.of.pieces Supplier C: PP; nb.of.pieces

Given these are categories that best distinguish the different suppliers from one another and offer some characteristics, the cosmetics company could use this to benchmark them, which could be extremely useful. catdes can be used on other categorical variables for such insights.

catdes_supplier<- catdes(raw_data, num.var=1)
Chi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrect
catdes_supplier
$test.chi2
                    p.value df
Raw.Material   9.049049e-05  4
Impermeability 1.088731e-02  2

$category
$category$`Supplier A`
                       Cla/Mod  Mod/Cla   Global      p.value    v.test
Raw.Material=PS       42.30769 37.93103 13.61257 0.0002998155  3.615459
Impermeability=Type 2 34.78261 27.58621 12.04188 0.0130149176  2.483361
Shape=Shape 2         26.66667 41.37931 23.56021 0.0213728107  2.301333
Raw.Material=ABS       0.00000  0.00000 10.99476 0.0254288561 -2.234825
Impermeability=Type 1 12.50000 72.41379 87.95812 0.0130149176 -2.483361

$category$`Supplier B`
                   Cla/Mod  Mod/Cla   Global     p.value    v.test
Raw.Material=ABS 100.00000 14.18919 10.99476 0.003330616  2.935453
Raw.Material=PS   57.69231 10.13514 13.61257 0.015928453 -2.410551
Shape=Shape 2     60.00000 18.24324 23.56021 0.002374481 -3.038894

$category$`Supplier C`
                 Cla/Mod Mod/Cla   Global    p.value   v.test
Raw.Material=PP 9.722222     100 75.39267 0.01626019 2.403023


$quanti.var
                  Eta2      P-value
nb.of.pieces 0.2137072 1.530822e-10

$quanti
$quanti$`Supplier A`
NULL

$quanti$`Supplier B`
                v.test Mean in category Overall mean sd in category Overall sd     p.value
nb.of.pieces -2.817845         3.959459     4.115183       1.240523   1.413225 0.004834708

$quanti$`Supplier C`
               v.test Mean in category Overall mean sd in category Overall sd      p.value
nb.of.pieces 6.345875         6.428571     4.115183       1.720228   1.413225 2.211654e-10


attr(,"class")
[1] "catdes" "list " 
plot(catdes_supplier, col = "darkblue")

15) To simultaneously take into account quantitative and categorical variables in the clustering you should use the HCPC function on the results of the FAMD ones. FAMD stands for Factorial Analysis of Mixed Data and is a PCA dedicated to mixed data. Explain what will be the impacts of such an analysis on the results?

In FAMD, the influence of both continous and categorical variables in the analysis is balanced. It means that both variables are on a equal foot to determine the dimensions of variability. Thus FAMD thus changes the correlation and variance structures of the previous analysis. The variance explained by each component reduces such that we would need 10 components to explain 95% of total variance.

Looking at the graphs, the Price continues to be strongly correlated to the first component, as are weight Length, Diameter, Impermeability and Raw.Materials. Shape is also somewhat well correlated to the first component and thus to Price. However, Finishing, Mature.Volume are not well represented on the map and Supplier and nb.of.pieces are correlated to the second component and thus uncorrelated to Price.

Furthermore, given many more components are taken into account to ensure information is not lost, the clustering is also affected and seems now less informative than previously.

res.famd <- FAMD (raw_data, sup.var = c(10), ncp = 10 , axes = c(1,2), row.w = NULL, tab.comp = NULL, graph = FALSE)
res.famd$eig
        eigenvalue percentage of variance cumulative percentage of variance
comp 1   4.6069336              32.906669                          32.90667
comp 2   1.6700311              11.928794                          44.83546
comp 3   1.4684324              10.488803                          55.32426
comp 4   1.2771099               9.122213                          64.44648
comp 5   1.0019966               7.157119                          71.60360
comp 6   0.8935056               6.382183                          77.98578
comp 7   0.7712154               5.508681                          83.49446
comp 8   0.6765127               4.832233                          88.32669
comp 9   0.6046173               4.318695                          92.64539
comp 10  0.4570087               3.264348                          95.90974
fviz_famd_var(res.famd, repel = TRUE)

fviz_famd_var(res.famd, "quanti.var", col.var = "cos2", gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"), repel = TRUE)

fviz_famd_var(res.famd, "quali.var", col.var = "cos2", gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"))

fviz_famd_ind(res.famd, col.ind="cos2", label=c("quali"), geom = "point", habillage = "none") + scale_color_gradient2(low="lightblue", mid="blue", high="darkblue", midpoint=0.6) + theme_minimal() 

res.hcpc.famd <- HCPC(res.famd, nb.clust = -1, graph = FALSE)
Chi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrectChi-squared approximation may be incorrect
plot.HCPC(res.hcpc.famd, choice = 'map', draw.tree = FALSE, select = c(1,10), title = '')

16) Perform a model to predict the Price. Explain how the previous analysis can help you to interpret the results.

We first split our datta into training and test sets. Here we will generate a random sample from the data which will consist of 70% to 90% of the rows in the original data to train the model on and will test on the remaining rows.

The variables we chose for our linear model will be those which we have seen are most siginificant for Price from our analysis - namely: - Price, Diameter, Length - Impermeability, Raw.Materials, Shape

Notice tht the R squared of the regression is satisfactory.

training_set <- sample_n(raw_data, round((runif(1,min=0.70,max=0.90) * dim(raw_data)[1]),0) )
dim(training_set) 
[1] 170  11
test_set <- raw_data[!(raw_data$Length %in% training_set$Length),]
dim(test_set) 
[1] 21 11
assertthat::are_equal(dim(training_set)[1]+ dim(test_set)[1] ,dim(raw_data)[1])
[1] TRUE
lm_fit <- lm(Price ~ Diameter + Length + weight + Raw.Material + Impermeability + Shape, data= training_set)
lm_prediction <- predict.lm(lm_fit, test_set)
summary(lm_fit)

Call:
lm(formula = Price ~ Diameter + Length + weight + Raw.Material + 
    Impermeability + Shape, data = training_set)

Residuals:
     Min       1Q   Median       3Q      Max 
-11.6778  -2.4214  -0.4382   2.0101  15.7151 

Coefficients:
                     Estimate Std. Error t value Pr(>|t|)    
(Intercept)           11.2532     1.1468   9.813  < 2e-16 ***
Diameter              -5.2654    12.7410  -0.413  0.67997    
Length                 1.0518     1.6000   0.657  0.51189    
weight                 1.2829     0.9686   1.324  0.18725    
Raw.MaterialPP        -2.2129     0.9992  -2.215  0.02820 *  
Raw.MaterialPS        -4.0605     1.3874  -2.927  0.00392 ** 
ImpermeabilityType 2   5.4117     1.5815   3.422  0.00079 ***
ShapeShape 2           0.5592     0.9362   0.597  0.55117    
ShapeShape 3           0.9838     1.7997   0.547  0.58538    
ShapeShape 4           7.0172     1.4573   4.815 3.38e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.897 on 160 degrees of freedom
Multiple R-squared:  0.7178,    Adjusted R-squared:  0.7019 
F-statistic: 45.21 on 9 and 160 DF,  p-value: < 2.2e-16

We now wish to assess the the linear model. Obviously, the quality of the results depend on size and the quality of the training set, however we can see that in general, it performs poorly for prices in the tails of the price distribution. We can view the quantiles to have an idea of the precision of the prediction. Note we can make use our PCA performed above to interpret why certain individuals have been projected well or poorly here. This could be particularly insightful for the tails, since large price are underestimated here and low prices are overestimated. We note that one solution could be to add more weight to the data in the tails to regularise training set.

plot(test_set$Price,lm_prediction, ylab = "Predicted", xlab ="Actual") + abline(a=0,b=1)
integer(0)

model_vs_test<- data_frame(lm_prediction,test_set$Price)
residual_lm <- model_vs_test %>% mutate("Residual" = (lm_prediction - test_set$Price))
plot(residual_lm$`test_set$Price`,residual_lm$Residual, xlab= "Actual", ylab = "Residual")

quantile(abs(residual_lm$Residual))
       0%       25%       50%       75%      100% 
 0.134359  1.146660  2.312809  4.879229 10.314484 

17) If someone ask you why you did one global model and not one model per supplier, what is your answer?

Below we count the amount of data points we have for each supplier. We see Supplier C only has 14 data points, thus performing a model per supplier may lead to overfitting and fail to predict future observations reliably. If we regularised our data, we may be able to perform this type of model.

raw_data %>% group_by(Supplier) %>%  count()

18) These data contained missing values. One representative in the company suggests either to put 0 in the missing cells or to impute with the median of the variables. Comment. For the categorical variables with missing values, it is decided to create a new category “missing”. Comment.

Imputation with the median or replacing the missing values with 0 will have similar results to mean imputation in the sense that replacing with a constant alters the structure structure of the dataset. The variability in the data will be reduced and the relationships between variables will be igorned. Thus the variance estimates will tend to be underestimated and the magnitude of the correlation also decrease. Overall, the variance and correlation structure will be distorted but this procedure can give satisfactory results if not too much data is missing.

Creating a new category for each variable which contains one or more missing values can be an effective way of tackling missing values. A variable will have an extra category if at least one individual possesses missing data for variable this variable. It will therefore be possible to conduct the MCA on the new data table by considering the missing categories in the same way as the other categories. In practice, this detects the structure of the missing data in the dataset and enables the structure of the missing data to be viewed. Examining the organisation of missing data and understanding why certain individuals did not answer a group of questions can be informative but it also leads to excessive structuring of the dataset and can conceal the information held within the answers.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIFNjcmV3IENhcHMgQ2FzZSBTdHVkeSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKKipMaWJyYXJpZXMqKgoKV2UgbG9hZCB0aGUgcGFja2FnZXMgcmVsZXZhbnQgZm9yIHRoZSBleGVyY2lzZS4gCgpgYGB7cn0KCmxpYnJhcnkoRmFjdG9NaW5lUikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkobW9tZW50cykKCmBgYAoKCioqU2NyZXcgQ2FwcyBEYXRhKioKClRoZSBkYXRhIFNjcmV3Q2FwLmNzdiBjb250YWlucyAxOTUgbG90cyBvZiBzY3JldyBjYXBzIGRlc2NyaWJlZCBieSAxMSB2YXJpYWJsZXMuIERpYW1ldGVyLCB3ZWlnaHQsIGxlbmd0aCBhcmUgdGhlIHBoeXNpY2FsIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgY2FwOyBuYi5vZi5waWVjZXMgY29ycmVzcG9uZHMgdG8gdGhlIG51bWJlciBvZiBlbGVtZW50cyBvZiB0aGUgY2FwOyBNYXR1cmUudm9sdW1lIGNvcnJlc3BvbmRzIHRvIHRoZSBudW1iZXIgb2YgY2FwcyBvcmRlcmVkIGFuZCBib3VnaHQgYnkgdGhlIGNvbXBhbnkgKG51bWJlciBpbiB0aGUgbG90KS4KCldlIGZpcnN0IGV4cGxvcmUgdGhlIGRhdGFzZXQgLSAKCmBgYHtyfQoKcmF3X2RhdGEgPC0gcmVhZC50YWJsZSgiU2NyZXdDYXBzLmNzdiIsaGVhZGVyPVRSVUUsIHNlcD0iLCIsIGRlYz0iLiIsIHJvdy5uYW1lcz0xKQpoZWFkKHJhd19kYXRhKQpkaW0ocmF3X2RhdGEpCnN1bW1hcnkocmF3X2RhdGEpCmBgYAoKKioyKSBXZSBzdGFydCB3aXRoIHVuaXZhcmlhdGUgYW5kIGJpdmFyaWF0ZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzLiBVc2luZyBhcHByb3ByaWF0ZSBwbG90KHMpIG9yIHN1bW1hcmllcyBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMuKioKCiphKSBIb3cgaXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgUHJpY2U/IENvbW1lbnQgeW91ciBwbG90IHdpdGggcmVzcGVjdCB0byB0aGUgcXVhcnRpbGVzIG9mIHRoZSBQcmljZS4qCgpGcm9tIHRoZSBxdWFudGlsZSBkYXRhLCB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIGFyZSBnaXZlbiBieTogbWVkaWFuLCAxUSBhbmQgM1EgIGFzIDE0LjQzMiwgMTEuODY0IGFuZCAxOS4wNCByZXNwZWN0aXZlbHkuIAoKVGhlIHBsb3RzLCB0aGUga3VydG9zaXMgYW5kIHRoZSBza2V3bmVzcyBwYXJhbWV0ZXJzIHN1Z2dlc3QgdGhlIHByaWNlIGZvbGxvd3MgYSBiaW1vZGFsIGRpc3RyaWJ1dGlvbiB0aGF0IGlzICJza2V3ZWQgcmlnaHQiLiAKClRoZSBtYWpvciBtb2RlIGlzIGFyb3VuZCAxNCBhbmQgdGhlIGFudGltb2RlIGlzIGFyb3VuZCAyOS4gRnVydGhlcm1vcmUsIDUwJSBvZiB0aGUgcHJpY2VzIGluIHRoZSByYW5nZSAxMS44NjQgYW5kIDE5LjA0LiBUaGlzIGlzIGNvbnNpc3RlbnQgd2l0aCBncmFwaCB3aGVyZSB0aGUgbWFqb3JpdHkgb2YgdGhlIGRlbnNpdHkgaXMgY29uY2VudHJhdGVkIGluc2lkZSB0aGlzIHJhbmdlIGFuZCBhIGxvbmcgcmlnaHQgdGFpbCBvZiBwcmljZXMgb3V0c2lkZS4gCgpUaGUgYm94cGxvdCBzdXBwb3J0cyB0aGlzIGFuYWx5aXMgYW5kIHN1Z2dlc3RzIHRoZSB2YWx1ZXMgaW4gdGhlIHRhaWwgYXJlIG91dGxpZXJzLiAKCmBgYHtyfQoKcHJpY2VfZGVuc2l0eSA8LSBnZ2RlbnNpdHkocmF3X2RhdGEseD0iUHJpY2UiLHkgPSAiLi5jb3VudC4uIiwKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9ImRhcmtibHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgZmlsbD0ibGlnaHRibHVlIixzaXplPTAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhPTAuMiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIlNjcmV3IENhcCBQcmljZSBEaXN0cmlidXRpb24iLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAic29saWQiLCBhZGQgPSBjKCJtZWRpYW4iKSkrIGZvbnQoInRpdGxlIiwgc2l6ZSA9IDEyLGZhY2U9ImJvbGQiKQogIAoKICAKCnByaWNlX2JveHBsb3QgPC0gZ2dib3hwbG90KHJhd19kYXRhJFByaWNlLCB3aWR0aCA9IDAuMSwgZmlsbCA9ImxpZ2h0Z3JheSIsIG91dGxpZXIuY29sb3VyID0gImRhcmtibHVlIiwgb3V0bGllci5zaGFwZT00LjIsIHlsYWIgPSAiUHJpY2UiLCB4bGFiID0gIlNjcmV3IENhcHMiICwgdGl0bGUgPSAiUHJpY2UgQm94IFBsb3QiKSArIHJvdGF0ZSgpICsgZm9udCgidGl0bGUiLCBzaXplID0gMTIsZmFjZT0iYm9sZCIpCgoKCnByaWNlX3F1YW50aWxlIDwtIHF1YW50aWxlKHJhd19kYXRhJFByaWNlKQoKZ2dhcnJhbmdlKHByaWNlX2RlbnNpdHksIHByaWNlX2JveHBsb3QsIG5jb2wgPSAxLCBucm93ID0gMikKCnByaWNlX3F1YW50aWxlCnNrZXduZXNzKHJhd19kYXRhJFByaWNlKQprdXJ0b3NpcyhyYXdfZGF0YSRQcmljZSkKCgpgYGAKCgoKKmIpIERvZXMgdGhlIFByaWNlIGRlcGVuZCBvbiB0aGUgTGVuZ3RoPyB3ZWlnaHQ/KgoKV2UgZXhhbWluZSBQcmljZSB2cy4gTGVuZ3RoLCBsb2coUHJpY2UpIHZzLiBsb2coTGVuZ3RoKTsgUHJpY2UgdnMuIHdlaWdodCwgbG9nKFByaWNlKSB2cy4gbG9nKHdlaWdodCkgYW5kIHByb3ZpZGUgdGhlIHN1bW1hcnkgZm9yIGVhY2guIAoKVGhlIHBsb3RzIHN1Z2dlc3Qgc29tZXdoYXQgb2YgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdmFyaWFibGVzIGFuZCBvYnNlcnZpbmcgdGhlIFItc3FhdXJlZCB2YWx1ZXMgYW5kIHRoZSByZXN1bHRzIG9mIHRoZSBGIGFuZCBUIHRlc3RzIGNvbmZpcm0gdGhpcyB0byBhIGhpZ2ggZGVncmVlIG9mIHNpZ25pZmljYW5jZS4gCgpgYGB7cn0KcHJpY2VfbGVuZ3RoIDwtIGdncGxvdChyYXdfZGF0YSwgYWVzKHg9TGVuZ3RoLCB5PVByaWNlKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2Q9bG0sIGNvbG9yPSJkYXJrZ3JlZW4iKSsgdGhlbWVfbWluaW1hbCgpCnByaWNlX2xlbmd0aF9sb2cgPC0gZ2dwbG90KHJhd19kYXRhLCBhZXMoeD1sb2coTGVuZ3RoKSwgeT1sb2coUHJpY2UpKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2Q9bG0sIGNvbG9yPSJkYXJrZ3JlZW4iKSsgdGhlbWVfbWluaW1hbCgpCnByaWNlX3dlaWdodCA8LSBnZ3Bsb3QocmF3X2RhdGEsIGFlcyh4PXdlaWdodCwgeT1QcmljZSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLGNvbG9yPSJyZWQiKSt0aGVtZV9taW5pbWFsKCkKcHJpY2Vfd2VpZ2h0X2xvZyA8LSBnZ3Bsb3QocmF3X2RhdGEsIGFlcyh4PWxvZyh3ZWlnaHQpLCB5PWxvZyhQcmljZSkpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZD1sbSxjb2xvcj0icmVkIikrdGhlbWVfbWluaW1hbCgpCgpnZ2FycmFuZ2UoZ2dhcnJhbmdlKHByaWNlX2xlbmd0aCwgcHJpY2VfbGVuZ3RoX2xvZywgbmNvbCA9IDIsIG5yb3cgPSAxKSwgZ2dhcnJhbmdlKHByaWNlX3dlaWdodCwgcHJpY2Vfd2VpZ2h0X2xvZywgbmNvbCA9IDIsIG5yb3cgPSAxKSwgbmNvbCA9IDEsIG5yb3cgPSAyKQoKc3VtbWFyeShsbShmb3JtdWxhID0gUHJpY2UgfiBMZW5ndGgsIHJhd19kYXRhKSkKc3VtbWFyeShsbShmb3JtdWxhID0gbG9nKFByaWNlKSB+IGxvZyhMZW5ndGgpLCByYXdfZGF0YSkpCnN1bW1hcnkobG0oZm9ybXVsYSA9IFByaWNlIH4gd2VpZ2h0LCByYXdfZGF0YSkpCnN1bW1hcnkobG0oZm9ybXVsYSA9IGxvZyhQcmljZSkgfiBsb2cod2VpZ2h0KSwgcmF3X2RhdGEpKQpgYGAKCgoqYykgRG9lcyB0aGUgUHJpY2UgZGVwZW5kIG9uIHRoZSBJbXBlcm1lYWJpbGl0eT8gU2hhcGU/KiAgCgpDb25jZXJuaW5nIEltcGVybWVhYmlsaXR5LCB0aGUgcGxvdHMgYmVsb3cgc2hvdyB0aGF0IHRoZXJlIGFyZSBzb21lIHN0cmlraW5nIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIHByaWNlIGRpc3RyaWJ1dGlvbiBmb3IgVHlwZSAxIGFuZCBUeXBlIDIsIGluIHBhcnRpY3VsYXIgb2JzZXJ2aW5nIHRoZSBtZWRpYW5zIGFuZCB0aGUgSVFSLiAKCkNvbmNlcm5pbmcgU2hhcGVzLCBpdCBpcyBkaWZmaWN1bHQgdG8gbWFrZSBhbnkgcmVhbCBjb25jbHVzaW9ucyByZWdhcmRpbmcgc2hhcGUgMyBhbmQgc2hhcGUgNCBnaXZlbiB0aGVyZSBhcmUgc28gZmV3IGRhdGEgcG9pbnRzLiBXZSB0dXJuIG91ciBhdHRlbnRpb24gdG8gU2hhcGUgMSBhbmQgU2hhcGUgMiAtIHRoZSBJUVIgZm9yIHRoZXNlIHR3byBzaGFwZXMgYXJlIHNlZW1pbmdseSBkaWZmZXJlbnQuIFRoaXMgaXMgY29uZmlybWVkIGJ5IHRoZSByZXN1bHQgb2YgdGhlIFQgVGVzdC4gCgoKYGBge3J9CmltcGVybWFiaWxpdHlfcGxvdF8xIDwtIGdnZG90cGxvdChyYXdfZGF0YSx4PSJJbXBlcm1lYWJpbGl0eSIseT0iUHJpY2UiLGNvbG9yID0gIkltcGVybWVhYmlsaXR5IiwgcGFsZXR0ZSA9ICJqY28iLGJpbndpZHRoID0gMSxsZWdlbmQ9Im5vbmUiKQpzaGFwZV9wbG90XzEgPC0gZ2dkb3RwbG90KHJhd19kYXRhLHg9IlNoYXBlIix5PSJQcmljZSIsY29sb3IgPSAiU2hhcGUiLCBwYWxldHRlID0gIm5wZyIsYmlud2lkdGggPSAxLGxlZ2VuZD0ibm9uZSIpCmltcGVybWFiaWxpdHlfcGxvdF8yIDwtIGdnYm94cGxvdChyYXdfZGF0YSx4PSJJbXBlcm1lYWJpbGl0eSIseT0iUHJpY2UiLGNvbG9yID0gIkltcGVybWVhYmlsaXR5IiwgcGFsZXR0ZSA9ICJqY28iLGxlZ2VuZD0ibm9uZSIpCnNoYXBlX3Bsb3RfMiA8LSBnZ2JveHBsb3QocmF3X2RhdGEseD0iU2hhcGUiLHk9IlByaWNlIixjb2xvciA9ICJTaGFwZSIsIHBhbGV0dGUgPSAibnBnIiwgbGVnZW5kID0gIm5vbmUiKQoKCmdnYXJyYW5nZShnZ2FycmFuZ2UoaW1wZXJtYWJpbGl0eV9wbG90XzEsaW1wZXJtYWJpbGl0eV9wbG90XzIsbmNvbCA9IDIsIG5yb3cgPSAxKSwKICAgICAgICAgICBnZ2FycmFuZ2Uoc2hhcGVfcGxvdF8xLHNoYXBlX3Bsb3RfMixuY29sID0gMiwgbnJvdyA9IDEpLAogICAgICAgICAgIG5jb2wgPSAxLCBucm93ID0gMikKCnN1bW1hcnkobG0oUHJpY2V+IEltcGVybWVhYmlsaXR5LCBkYXRhPXJhd19kYXRhKSkKc3VtbWFyeShsbShQcmljZX4gU2hhcGUsIGRhdGE9cmF3X2RhdGEpKQoKYGBgCgoqZCkgV2hpY2ggaXMgdGhlIGxlc3MgZXhwZW5zaXZlIFN1cHBsaWVyPyoKClRoZSBhbnN3ZXIgdG8gdGhpcyBxdWVzdGlvbiBkZXBlbmRzIG9uIHRoZSBkZWZpbml0aW9uIG9mIGV4cGVuc2l2ZS4gV2UgYWxzbyBub3RlIHRoYXQgdGhlcmUgYXJlIGZldyBkYXRhIHBvaW50cyBmb3IgU3VwcGxpZXIgQy4KCkZpcnN0LCBleGFtaW5lIHRoZSBmb2xsb3dpbmcgYWJzb2x1dGUgbWV0cmljcyAodGhpcyBjYW4gYmUgc2VlbiB2aWEgdGhlIGJveHBsb3QpCjEpIEFic29sdXRlIHByaWNlIC0gU3VwcGxpZXIgQiBjaGVhcGVzdCAoNi40Nzc0NTEpLiBIb3dldmVyLCBTdXBwbGllciBCIGlzIGFsc28gdGhlIHN1cHBsaWVyIHdoaWNoIGhhcyB0aGUgaGlnaGVzdCBhYnNvbHV0ZSBwcmljZSAoNDYuNjEwMzcyKQoyKSBBdmVyYWdlIFByaWNlIC0gU3VwcGxpZXIgQyBjaGVhcGVzdCAoMTQuODg4NjkpCgpTZWNvbmQsIGV4YW1pbmUgdGhlIGZvbGxvd2luZyByZWxhdGl2ZSBtZXRyaWNzOiAgCjMpIEF2ZXJhZ2UgUHJpY2UgLyBVbml0IExlbmd0aCAtIFN1cHBsaWVyIEEgKDEuNTA1MDQzKQo0KSBBdmVyYWdlIFByaWNlIC8gVW5pdCB3ZWlnaHQgLSBTdXBwbGllciBBICg5LjAxMzkwMikKNSkgQXZlcmFnZSBQcmljZSAvIFVuaXQgRGlhbWV0ZXIgLSBTdXBwbGllciBBICgxMS45NTYzMikKNikgQXZlcmFnZSBQcmljZSAvIFVuaXQgTWF0dXJlLlZvbHVtZSAtIFN1cHBsaWVyIEIgKDEuNjYzMzA1KQoKVGhlIHJlc3VsdCBhYm92ZSBzdWdnZXN0IFN1cHBsaWVyIEEgaGFzIHRoZSBjaGVhcGVzdCBhdmVyYWdlIHByaWNlIHBlciB1bml0IG9mIHByb2R1Y3Rpb24uCgpUaGUgYW5hbHlzaXMgaG93ZXZlciBpcyBub3QgY29tcGxldGUgZ2l2ZW4gd2UgZG8gbm90IGhhdmUgYSBkZWZpbml0aW9uIG9mIGNoZWFwZXN0IHByaWNlLiBFdmVuIHRoZSBzY2F0dGVyIGFuZCBib3ggcGxvdHMgYmVsb3cgc3VnZ2VzdCBzdXBwbGllcnMgbWF5IGNhdGVyIHRvIHNwZWNpZmljIHByb2R1Y3QgcmFuZ2VzLiBXZSBoYXZlIG5vdCBwZXJmb3JtZWQgIHN0YXRpc3RpY2FsIHRlc3RzIHRvIGV4YW1pbmUgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGVzZSBkaWZmZXJlbmNlcy4gQ2F0ZWdvcmljYWwgZGF0YSBjb3VsZCBwcm92aWRlIHNvbWUgaW5zaWdodHMgaW50byBjaGVhcGVzdCBwcmljZSBmb3IgY2VydGFpbiBwcm9kdWN0IGZlYXR1cmVzLiAKCmBgYHtyfQoKc3VwcGxpZXJfcGxvdF8xIDwtIGdnYm94cGxvdChyYXdfZGF0YSx4PSJTdXBwbGllciIseT0iUHJpY2UiLGNvbG9yID0gIlN1cHBsaWVyIiwgcGFsZXR0ZSA9IGMoImRhcmtibHVlIiwicmVkIiwiZGFya2dyZWVuIiksbGVnZW5kPSJub25lIikgKyByb3RhdGUoKQpzdXBwbGllcl9wbG90XzIgPC0gZ2dzY2F0dGVyKHJhd19kYXRhLHg9Ikxlbmd0aCIseT0iUHJpY2UiLGNvbG9yID0gIlN1cHBsaWVyIiwgcGFsZXR0ZSA9IGMoImRhcmtibHVlIiwicmVkIiwiZGFya2dyZWVuIikseHNjYWxlPSAibG9nMTAiLCB5c2NhbGU9ImxvZzEwIikKc3VwcGxpZXJfcGxvdF8zIDwtIGdnc2NhdHRlcihyYXdfZGF0YSx4PSJ3ZWlnaHQiLHk9IlByaWNlIixjb2xvciA9ICJTdXBwbGllciIsIHBhbGV0dGUgPSBjKCJkYXJrYmx1ZSIsInJlZCIsImRhcmtncmVlbiIpLHhzY2FsZT0gImxvZzEwIiwgeXNjYWxlPSJsb2cxMCIpCnN1cHBsaWVyX3Bsb3RfNCA8LSBnZ3NjYXR0ZXIocmF3X2RhdGEseD0iRGlhbWV0ZXIiLHk9IlByaWNlIixjb2xvciA9ICJTdXBwbGllciIsIHBhbGV0dGUgPSBjKCJkYXJrYmx1ZSIsInJlZCIsImRhcmtncmVlbiIpLHhzY2FsZT0gImxvZzEwIiwgeXNjYWxlPSJsb2cxMCIpCgpzdXBwbGllcl9zdGF0aXN0aWNzIDwtIHJhd19kYXRhICU+JSBncm91cF9ieShTdXBwbGllcikgJT4lICBzdW1tYXJpc2UoICJBdmVyYWdlIFByaWNlIiA9IG1lYW4oUHJpY2UpLCAiQXZlcmFnZSBMZW5ndGgiID0gbWVhbihMZW5ndGgpLCJBdmVyYWdlIHdlaWdodCIgPSBtZWFuKHdlaWdodCksIkF2ZXJhZ2UgRGlhbWV0ZXIiID0gbWVhbihEaWFtZXRlciksIkF2ZXJhZ2UgTWF0dXJlLlZvbHVtZSIgPSBtZWFuKE1hdHVyZS5Wb2x1bWUpICAsIkF2ZXJhZ2UgUHJpY2UgLyBMZW5ndGgiID0gbWVhbihQcmljZSkvbWVhbihMZW5ndGgpLCAiQXZlcmFnZSBQcmljZSAvIHdlaWdodCIgPSBtZWFuKFByaWNlKS9tZWFuKHdlaWdodCksICJBdmVyYWdlIFByaWNlIC8gRGlhbWV0ZXIiID0gbWVhbihQcmljZSkvbWVhbihEaWFtZXRlciksIkF2ZXJhZ2UgUHJpY2UgLyBNYXR1cmUuVm9sdW1lIiA9IG1lYW4oUHJpY2UpL21lYW4oTWF0dXJlLlZvbHVtZSkpCgpzdXBwbGllcl9wbG90XzEKc3VwcGxpZXJfcGxvdF8yCnN1cHBsaWVyX3Bsb3RfMwpzdXBwbGllcl9wbG90XzQKaGVhZChzdXBwbGllcl9zdGF0aXN0aWNzKQoKYGBgCgoKCioqMykgT25lIGltcG9ydGFudCBwb2ludCBpbiBleHBsYW5hdG9yeSBkYXRhIGFuYWx5c2lzIGNvbnNpc3RzIGluIGlkZW50aWZ5aW5nIHBvdGVudGlhbCBvdXRsaWVycy4gQ291bGQgeW91IGdpdmUgcG9pbnRzIHdoaWNoIGFyZSBzdXNwZWN0IHJlZ2FyZGluZyB0aGUgTWF0dXJlLlZvbHVtZSB2YXJpYWJsZT8gR2l2ZSB0aGUgY2hhcmFjdGVyaXN0aWNzIChvdGhlciBmZWF0dXJlcykgb2YgdGhlIG9ic2VydmF0aW9ucyB0aGF0IHNlZW0gc3VzcHNlY3QqKgoKVGhlcmUgYXJlIGZvdXIgZGF0YSBwb2ludHMgd2hpY2ggc2VlbSBzdXNwZWN0IC0gdGhleSBoYXZlIHRoZSBzYW1lIGNoYXJhY3RlcmlzdGljcyBmb3IgRGlhbWV0ZXIsIHdlaWdodCwgbmIub2YucGllY2VzLCBJbXBlcm1lYWJpbGl0eSwgRmluaXNoaW5nLCBSYXcuTWF0ZXJpYWwgYW5kIE1hdHVyZS5Wb2x1bWUuIFRoZXkgZGlmZmVyIGluIHRoZWlyIHN1cHBsaWVyLCBwcmljZSBhbmQgbGVuZ3RoLiBUaGVzZSBzdWdnZXN0IHNvbWUgZXJyb3IgaW4gY29sbGF0aW5nIHRoZSBkYXRhIChzeXN0ZW0gZXJyb3IgLyBkZWZhdWx0IGRhdGEpLiAKCmBgYHtyfQoKTWF0dXJlLlZvbHVtZV9wbG90IDwtIGdnaGlzdG9ncmFtKHJhd19kYXRhLHg9Ik1hdHVyZS5Wb2x1bWUiLHk9Ii4uY291bnQuLiIsIGNvbG9yID0gImRhcmtibHVlIiwgZmlsbCA9ICJsaWdodGdyZXkiKSArIHRoZW1lX21pbmltYWwoKQpNYXR1cmUuVm9sdW1lX3Bsb3QKcmF3X2RhdGEgJT4lIGZpbHRlciAoTWF0dXJlLlZvbHVtZSA+IDZlKzA1ICkgCgpgYGAKCgpGb3IgdGhlIHJlc3Qgb2YgdGhlIGFuYWx5c2lzLCB0aGUgNCBkYXRhIHBvaW50cyBhYm92ZSBhcmUgZGlzcmVnYXJkZWQuCgpgYGB7cn0KcmF3X2RhdGEgPC0gcmF3X2RhdGEgJT4lIGZpbHRlciAoTWF0dXJlLlZvbHVtZSA8IDZlKzA1ICkKYGBgCgoKV2Ugd2lsbCBxdWlja2x5IGNoZWNrIHRoYXQgdGhlcmUgYXJlIG5vIG90aGVyIG5vdGljZWFibGUgb3V0bGllcnMgLSB0aGlzIGlzIGluZGVlZCB0aGUgY2FzZS4gIAogCmBgYHtyfQoKY2hlY2tfMSA8LSBnZ2hpc3RvZ3JhbShyYXdfZGF0YSx4PSJMZW5ndGgiLHk9Ii4uY291bnQuLiIsIGNvbG9yID0gImRhcmtibHVlIiwgZmlsbCA9ICJsaWdodGdyZXkiKSArIHRoZW1lX21pbmltYWwoKQpjaGVja18yIDwtIGdnaGlzdG9ncmFtKHJhd19kYXRhLHg9IkRpYW1ldGVyIix5PSIuLmNvdW50Li4iLCBjb2xvciA9ICJkYXJrYmx1ZSIsIGZpbGwgPSAibGlnaHRncmV5IikgKyB0aGVtZV9taW5pbWFsKCkKY2hlY2tfMyA8LSBnZ2hpc3RvZ3JhbShyYXdfZGF0YSx4PSJ3ZWlnaHQiLHk9Ii4uY291bnQuLiIsIGNvbG9yID0gImRhcmtibHVlIiwgZmlsbCA9ICJsaWdodGdyZXkiKSArIHRoZW1lX21pbmltYWwoKQpjaGVja180IDwtIGdnaGlzdG9ncmFtKHJhd19kYXRhLHg9Im5iLm9mLnBpZWNlcyIseT0iLi5jb3VudC4uIiwgY29sb3IgPSAiZGFya2JsdWUiLCBmaWxsID0gImxpZ2h0Z3JleSIsYmlucz00MCkgKyB0aGVtZV9taW5pbWFsKCkKCmdnYXJyYW5nZShnZ2FycmFuZ2UoY2hlY2tfMSxjaGVja18yLG5jb2w9Mixucm93PTEpLGdnYXJyYW5nZShjaGVja18zLGNoZWNrXzQsbmNvbD0yLG5yb3c9MSksbmNvbD0xLG5yb3c9MikKCmBgYAoKCioqNCkgUGVyZm9ybSBhIFBDQSBvbiB0aGUgZGF0YXNldCBTY3Jld0NhcCwgZXhwbGFpbiBicmllZmx5IHdoYXQgYXJlIHRoZSBhaW1zIG9mIGEgUENBIGFuZCBob3cgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFyZSBoYW5kbGVkPyoqCgpQcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyAoUENBKSBpcyBhIHRlY2huaXF1ZSBmb3IgdGFraW5nIGhpZ2gtZGltZW5zaW9uYWwgZGF0YSwgYW5kIHVzaW5nIHRoZSBkZXBlbmRlbmNpZXMgYmV0d2VlbiB0aGUgdmFyaWFibGVzIHRvIHJlcHJlc2VudCBpdCBpbiBhIG1vcmUgdHJhY3RhYmxlLCBsb3dlci1kaW1lbnNpb25hbCBmb3JtLCB3aXRob3V0IGxvc2luZyB0b28gbXVjaCBpbmZvcm1hdGlvbiAtIHdlIHRyeSBjYXB0dXJlIHRoZSBlc3NlbmNlIG9mIGhpZ2ggZGltZW50aW9uYWwgZGF0YSBpbiBhIGxvdyBkaW1lbnNpb25hbCByZXByZXNlbnRhdGlvbi4gVGhlIGFpbSBvZiBQQ0EgaXMgdG8gZHJhdyBjb25jbHVzaW9ucyBmcm9tIHRoZSBsaW5lYXIgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcyBieSBkZXRlY3RpbmcgdGhlIHByaW5jaXBhbCBkaW1lbnNpb25zIG9mIHZhcmlhYmlsaXR5LiBUaGlzIG1heSBiZSBmb3IgY29tcHJlc3Npb24sIGRlbm9pc2luZywgZGF0YSBjb21wbGV0aW9uLCBhbm9tYWx5IGRldGVjdGlvbiBvciBmb3IgcHJlcHJvY2Vzc2luZyBiZWZvcmUgc3VwZXJ2aXNlZCBsZWFybmluZyAoaW1wcm92ZSBwZXJmb3JtYW5jZSAvIHJlZ3VsYXJpemF0aW9uIHRvIHJlZHVjZSBvdmVyZml0dGluZykuCgpUaGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGNhbm5vdCBiZSByZXByZXNlbnRlZCBpbiB0aGUgc2FtZSB3YXkgYXMgdGhlIHN1cHBsZW1lbnRhcnkgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyBzaW5jZSBpdCBpcyBub3QgcG9zc2libGUgdG8gY2FsY3VsYXRlIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgYW5kIHRoZSBwcmluY2lwYWwgY29tcG9uZW50cy4gVGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBoZXJlIGFyZSBoYW5kbGVkIGFzIHN1cHBsZW1ldGFyeSB2YXJpYWJsZXMgb24gYSBwdXJlbHkgaWxsdXN0cmF0aXZlIGJhc2lzICAtIHRoZXkgYXJlIG5vdCB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgYmV0d2VlbiBpbmlkaXZpZHVhbHMuIFdlIHJlcHJlc2VudCBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGF0IHRoZSBiYXJ5Y2VudHJlIG9mIGFsbCB0aGUgaW5kaXZpZHVhbHMgcG9zc2Vzc2luZyB0aGF0IHZhcmlhYmxlLiBBIGNhdGVnb3JpY2FsIHZhcmlhYmxlIG9uIHRoZSBQQ0EgcGVyZm9ybWVkIGJlbG93IGNhbiB0aGVyZWZvcmUgYmUgcmVnYXJkZWQgYXMgdGhlIG1lYW4gaW5kaXZpZHVhbCBvYnRhaW5lZCBmcm9tIHRoZSBzZXQgb2YgaW5kaXZpZHVhbHMgd2hvIGhhdmUgaXQuCgpHaXZlbiBvdXIgdWx0aW1hdGUgZ29hbCBoZXJlIGlzIHRvIGV4cGxvcmUgZGF0YSBwcmlvciB0byBhIG11bHRpcGxlIHJlZ3Jlc3Npb24sIGl0IGlzIGFkdmlzYWJsZSB0byBjaG9vc2UgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBmb3IgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgYXMgYWN0aXZlIHZhcmlhYmxlcyBmb3IgUENBLCBhbmQgdG8gcHJvamVjdCB0aGUgdmFyaWFibGUgdG8gYmUgZXhwbGFpbmVkICh0aGUgZGVwZW5kZW50IHZhcmlhYmxlKSBhcyBhIHN1cHBsZW1lbnRhcnkgdmFyaWFibGUuIFRoaXMgZ2l2ZXMgc29tZSBpZGVhIG9mIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gZXhwbGFuYXRvcnkgdmFyaWFibGVzIGFuZCB0aHVzIG9mIHRoZSBuZWVkIHRvIHNlbGVjdCBleHBsYW5hdG9yeSB2YXJpYWJsZXMuIFRoaXMgYWxzbyBnaXZlcyB1cyBhbiBpZGVhIG9mIHRoZSBxdWFsaXR5IG9mIHRoZSByZWdyZXNzaW9uOiBpZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGlzIGFwcHJvcHJpYXRlbHkgcHJvamVjdGVkLCBpdCB3aWxsIGJlIGEgd2VsbC1maXR0ZWQgbW9kZWwuIFRodXMgd2Ugc2VsZWN0IFByaWNlIGFzIGEgc3VwcGxlbWVudGFyeSB2YXJpYWJsZS4gCgpUaGUgZGF0YXNldCBpbiB0aGlzIGV4ZXJjaXNlIGNvbnRhaW5zIDYgc3VwcGxlbWVudGFyeSB2YXJpYWJsZXM6Ci0gMSBxdWFudGl0YXRpdmUgdmFyaWFibGUgKFByaWNlKS4KLSA1IHF1YWxpdGF0aXZlIHZhcmlhYmxlcyAoU3VwcGxpZXIsIFNoYXBlLCBJbXBlcm1lYWJpbGl0eSBhbmQgRmluaXNoaW5nKS4gIAoKCmBgYHtyfQoKcmVzLnBjYSA8LSBQQ0EocmF3X2RhdGEscXVhbGkuc3VwID0gYygxLDUsNiw3LDkpLHF1YW50aS5zdXAgPSAxMCwgZ3JhcGggPSBGQUxTRSxzY2FsZSA9IFRSVUUpCgpmdml6X3BjYV9pbmQocmVzLnBjYSwgY29sLmluZD0iY29zMiIsIGxhYmVsPWMoInF1YWxpIiksIGdlb20gPSAicG9pbnQiLCB0aXRsZSA9ICJJbmRpdmlkdWFsIGZhY3RvciBtYXAgKFBDQSkiLCBoYWJpbGxhZ2UgPSAibm9uZSIpICsgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGxvdz0ibGlnaHRibHVlIiwgbWlkPSJibHVlIiwgaGlnaD0iZGFya2JsdWUiLCBtaWRwb2ludD0wLjYpICsgdGhlbWVfbWluaW1hbCgpIApwbG90LlBDQShyZXMucGNhLGNob2l4ID0gYygiaW5kIiksaW52aXNpYmxlID0gYygiaW5kIikpK3RoZW1lX21pbmltYWwoKQpwbG90LlBDQShyZXMucGNhLGNob2l4ID0gYygidmFyIikpK3RoZW1lX21pbmltYWwoKQoKCmBgYAoKCgoqKjUpIENvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMgYW5kIGNvbW1lbnQgaXQgd2l0aCByZXNwZWN0IHRvIHRoZSBjb3JyZWxhdGlvbiBjaXJjbGUqKgoKVGhlIGZpcnN0IHRhc2sgaXMgdG8gY2VudGVyIGFuZCBzdGFuZGFyZGl6ZSB0aGUgdmFyaWFibGVzLiBUaGVuIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggaXMgY29tcHV0ZWQuIEFsbCB2YXJpYWJsZSB2ZWN0b3JzIGFyZSBxdWl0ZSBuZWFyIHRvIHRoZSBib3VuZGFyeSBvZiB0aGUgY29ycmVsYXRpb24gY2lyY2xlIG9uIHRoZSB2YXJpYWJsZXMgcGxvdCAtIHRodXMgdGhlIHZhcmlhYmxlcyBhcmUgcmVsYXRpdmVseSB3ZWxsIHByb2plY3RlZCBvbiB0aGUgMiBkaW1lbnNpb25hbCBzdWJzcGFjZS4gV2Ugbm93IHR1cm4gb3VyIGF0dGVudGlvbiB0byBjb3JyZWxhdGlvbnMgYmV0d2VlbiB2YXJpYWJsZXMuIApUaGUgY29ycmVsYXRpb25zIGNhbiBiZSB2aXN1YWxpc2VkIHRocm91Z2ggdGhlIGFuZ2xlcyBiZXR3ZWVuIHZhcmlhYmxlcyBvbiB0aGUgY29ycmVsYXRpb24gbWF0cml4LiBUaGlzIGNhbiBiZSByZWxhdGVkIHRvIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggKHNtYWxsIGFuZ2xlcyBzdWdnZXN0IGxhcmdlIHBvc2l0aXZlIGNvcnJlbGF0aW9uLCA5MCBkZWdyZWUgYW5nbGVzIHN1Z2dlc3Qgbm8gY29ycmVsYXRpb24sIDE4MCBkZWdyZWUgYW5nbGVzIHN1Z2dlc3QgbGFyZ2UgbmVnYXRpdmUgY29ycmVsYXRpb24pLgotIERpYW1ldGVyLCBMZW5ndGggYW5kIHdlaWdodCBleHBvc2UgdmVyeSBzdHJvbmcgY29ycmxlYXRpb246IHRoZSBhbmdsZSBiZXR3ZWVuIHRoZW0gaXMgY2xvc2UgdG8gMCwgc3VnZ2VzdGluZyBjb3JyZWxhdGlvbiBjbG9zZSB0byAxLiBUaGVzZSBhcmUgYWxsIHZlcnkgaGlnaGx5IGNvcnJlbGF0ZWQgdG8gdGhlIGZpcnN0IFByaW5jaXBhbCBDb21wb25lbnQuCi0gVGhlIHRocmVlIHZhcmlhYmxlcyBhYm92ZSBhcmUgYXQgYW4gYW5nbGUgc2lnaHRseSB3aWRlciB0aGFuIGEgcmlnaHQgYW5nbGUgdG8gYm90aCBuYi5vZi5waWVjZXMgYW5kIE1hdHVyZS5Wb2x1bWUgaW4gdGhlIGNpcmxjZSB3aGljaCBzdWdnZXN0cyBzbGlnaHRseSBuZWdhdGl2ZSBjb3JyZWxhdGlvbi4KLSBQcmljZSBpcyBoaWdobGlnaGx5IGNvcnJlbGF0ZCB0byB0aGUgdGhyZWUgdmFyaWFibGVzIGFib3ZlCi0gRXF1YWxseSwgTWF0dXJlLlZvbHVtZSBhbmQgbmIub2YucGllY2VzIGFyZSBhdCBhIHNsaWdodGx5IHdpZGVyIGFuZ2xlIHRoYW4gYSByaWdodCBhbmdsZSB3aGljaCBzdWdnZXN0cyBzbGlnaHRseSBuZWdhdGl2ZSBjb3JyZWxhdGlvbiAtIHRoaXMgc3VnZ2VzdHMgdGhhdCB3aGVuIHRoZSBzY3JldyBjYXBzIGhhdmUgYSBoaWdoIG51bWJlciBvZiBwaWVjZXMsIHRoZSBjb21wYW55IG9yZGVycyBhIHNtYWxsZXIgdm9sdW1lIG9mIHRoZXNlLiBUaGVzZSBhcmUgd2VsbCBwcm9qZWN0ZWQgb24gdGhlIHNlY29uZCBwcmluY2lwYWwgY29tcG9uZW50LgoKYGBge3J9CmRvbiA8LSBhcy5tYXRyaXgocmF3X2RhdGFbLC1jKDEsNSw2LDcsOSwxMCldKSAlPiUgc2NhbGUoKQpkb25fY29ycmVsYXRpb24gPC0gY29yKGRvbikKZG9uX2NvcnJlbGF0aW9uCnBsb3QuUENBKHJlcy5wY2EsY2hvaXggPSBjKCJ2YXIiKSkrdGhlbWVfbWluaW1hbCgpCgpgYGAKCioqNikgT24gd2hhdCBraW5kIG9mIHJlbGF0aW9uc2hpcCBQQ0EgZm9jdXNlcz8gSXMgaXQgYSBwcm9ibGVtPyoqCgpQQ0EgZm9jdXNlcyBvbiB0aGUgbGluZWFyIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBjb250aW51b3VzIHZhcmlhYmxlcy4gR2l2ZW4gY29tcGxleCBsaW5rcyBhbHNvIGV4aXN0LCBzdWNoIGFzIHF1YWRyYXRpYyByZWxhdGlvbnNoaXBzLCBsb2dhcml0aG1pY3MsIGV4cG9uZW50aWFsIGZ1bmN0aW9ucywgYW5kIHNvIGZvcnRoLCB0aGlzIG1heSBzZWVtIHJlc3RyaWN0aXZlLCBidXQgaW4gcHJhY3RpY2UgbWFueSByZWxhdGlvbnNoaXBzIGNhbiBiZSBjb25zaWRlcmVkIGxpbmVhciwgYXQgbGVhc3QgZm9yIGFuIGluaXRpYWwgYXBwcm94aW1hdGlvbi4gSG93ZXZlciwgdGhlcmUgaXMgb2J2aW91c2x5IG5vbi1saW5lYXIgZGF0YXNldHMgZm9yIHdoaWNoIFBDQSB3aWxsIGhhdmUgcGl0ZmFsbHMgKGUuZy4gc3BpcmFsIGRhdGFzZXQpLiBGdXJ0aGVybW9yZSwgaW4gUENBIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBjYW5ub3QgYmUgYWN0aXZlIHZhcmlhYmxlcywgd2hpY2ggY2FuIGJlIHJlc3RyaWN0aXZlLiAKCioqNykgQ29tbWVudCB0aGUgUENBIG91dHB1dHMqKiAKCipDb21tZW50IHRoZSBwb3NpdGlvbiBvZiB0aGUgY2F0ZWdvcmllcyBJbXBlcm1lYWJpbGl0eT10eXBlIDIgYW5kIFJhdy5NYXRlcmlhbD1QUy4qCgpUaGUgY29vcmRpbmF0ZXMgZm9yIFR5cGUgMiBhcmUgKDMuMjggLCAwLjAxKSBmb3IgdGhlIGZpcnN0IHR3byBwcmluY2lwYWwgY29tcG9uZW50cy4gSXQgaGFzIGEgdmVyeSBzaWduaWZpY2FudCBwIHZhbHVlIGZvciBEaW0gMSBhbmQgdGh1cyAgdGhlIGNvb3JkaW5hdGUgaXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSAwIG9uIHRoZSBmaXJzdCBjb21wb25lbnQuIApUaGUgY29vcmRpbmF0ZSBmb3IgUFMgYXJlICgyLjY3LCAtMC4yNSkgZm9yIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMuIEl0IGhhcyBhIHZlcnkgc2lnbmlmaWNhbnQgcCB2YWx1ZSBmb3IgRGltIDEgYW5kIHRodXMgaGUgY29vcmRpbmF0ZSBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIDAgb24gdGhlIGZpcnN0IGNvbXBvbmVudC4gCgpGdXJ0aGVybW9yZSwgZ2l2ZW4gdGhlIGNvcnJlbGF0aW9uIGNpcmNsZSBzaG93cyBoaWdoIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGZpcnN0IGNvbXBvbmVudCBhbmQgcHJpY2UsIGRpYW1ldGVyLCBsZW5ndGggYW5kIHdlaWdodCwgdGhpcyBzdWdnZXN0IFR5cGUgMiBhbmQgUFMgaGF2ZSBoaWdoIHZhbHVlcyBmb3IgdGhlc2UgdmFyaWFibGVzLiAKCkluIGZhY3QsICBsb29raW5nIGF0IHRoZSBwLXZhbHVlcyB3ZSBjYW4gc2F5IHRoYXQgYm90aCBvZiB0aGUgY2F0ZWdvcmllcyBUeXBlIDEsIFR5cGUgMiwgUFMgYW5kIFBQIGhhdmUgY29vcmRpbmF0ZXMgdGhhdCBhcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSAwIG9uIHRoZSBmaXJzdCBjb21wb25lbnQuIEFzIHRoZSB2YWx1ZSBpcyBwb3NpdGl2ZSAobmVnYXRpdmUpIGZvciB0aGUgVHlwZSAyIC8gUFMgKFR5cGUgMSAvIFBQKSB3ZSBjYW4gc2F5IHRoYXQgdGhlIHJvd3Mgd2hpY2ggaW5jbHVkZSBUeXBlIDIgLyBQUyB0ZW5kZWQgdG8gaGF2ZSBwb3NpdGl2ZSBjb29yZGluYXRlcyAob3IgbmVnYXRpdmUsIHJlc3BlY3RpdmVseSkgb24gY29tcG9uZW50IDEgYW5kIHRodXMgYXJlIG1vcmUgY29ycmVsYXRlZCB3aXRoIHRoZSB2YXJpYWJsZXMgbWVudGlvbmVkIGFib3ZlIChwcmljZSxkaWFtZXRlciwgbGVuZ3RoIGFuZCB3ZWlnaHQpLgoKRmluYWxseSwgd2UgYWxzbyBjb25zaWRlciB0aGUgcmVzdWx0cyBmcm9tIHRoZSB3aWxrJ3MgdGVzdCBwZXJmb3JtZWQgaW4gdGhlIEZhY3RvSW52ZXN0aWdhdGUgcGFja2FnZS4gVGhpcyBpbmRpY2F0ZXMgd2hpY2ggdmFyaWFibGVzIGFyZSB0aGUgYmVzdCBzZXBlcmF0ZWQgb24gdGhlIHBsYW5lIChpLGUsIHdoaWNoIG9uZSBleHBsYWluIHRoZSBiZXN0IHRoZSBkaXN0YW5jZSBiZXR3ZWVuIGluZGl2aWR1YWxzLCBhbmQgdGhlIGJlc3QgcXVhbGl0YXRpdmUgdmFyaWFibGVzIHRvIGlsbHV0cmF0ZSBkaXN0YW5jZSBiZXR3ZWVuIGluZGl2aWR1YWxzIG9uIHRoZSBwbGFuZSBhcmUgSW1wZXJtZWFiaWxpdHkgYW5kIFJhdy5NYXRlcmlhbC4gCgpgYGB7cn0KcmVzLnBjYSRxdWFsaS5zdXAkY29vcmQKZGltZGVzYyhyZXMucGNhLCAxOjIpCndpbGtzLnAgPC1zdHJ1Y3R1cmUoYygzLjYzNDcxNjE0NDQ1MDIxZS0yNywgMS4yOTA1MDM0NzY2MTA4M2UtMjIsIDQuMDg4NzA2OTY1MzUwMTJlLTEwLCAKMy43MzE2MzgyODYxNDE3OWUtMDcsIDAuMjg0MjI3MzUzNjI0NDQ1KSwgLk5hbWVzID0gYygiSW1wZXJtZWFiaWxpdHkiLCAKIlJhdy5NYXRlcmlhbCIsICJTaGFwZSIsICJTdXBwbGllciIsICJGaW5pc2hpbmciKSkKd2lsa3MucApgYGAKCipDb21tZW50IHRoZSBwZXJjZW50YWdlIG9mIGluZXJ0aWEqCgpCZWxvdyBpbiB0aGUgU2NyZWUgcGxvdCB3ZSBzZWUgdGhlIHBlcmNlbnRhZ2Ugb2YgaW5lcnRpYSBleHBsYWluZWQgYnkgZWFjaCBjb21wb25lbnQuIFRoZSBmaXJzdCB0d28gY29tcG9uZW50cyBleHBsYWluIDgzLjQ4JSBvZiB0aGUgdG90YWwgZGF0YXNldCBpbmVydGlhbCAtIHRoaXMgbWVhbnMgdGhhdCA4My41MCUgb2YgdGhlIGluZGl2aWR1YWxzIChvciB2YXJpYWJsZXMpIGNsb3VkIHRvdGFsIHZhcmlhYmlsaXR5IGlzIGV4cGxhaW5lZCBieSB0aGlzIHBsYW5lLiBPdmVyIDk1JSBvZiB0aGUgdmFyaWFuY2UgY2FuIGJlIGV4cGxhaW5lZCB3aXRoIHRoZSAzIGZpcnN0IHN5bnRoZXRpYyB2ZWN0b3JzIGluIHRoZSBQQ0EuIAoKV2UgY2FuIGFsc28gc2VlIHRoYXQgdGhlIHZhcmlhbmNlIG9mIHRoZSBmaXJzdCBjb21wb25lbnQgaXMgZXhwbGFpbmVkIGluIG1ham9yaXR5IGJ5IERpYW1ldGVyLCBMZW5ndGggYW5kIHdlaWdodCBhcyBleHBlY3RlZC4gSW4gdGhlIHNlY29uZCBkaW1lbnNpb24gYnkgbmIub2YucGllY2VzIGFuZCBNYXR1cmUuVm9sdW1lLiAKCmBgYHtyfQpyZXMucGNhJGVpZwpmdml6X2VpZyhyZXMucGNhLCBhZGRsYWJlbHMgPSBUUlVFKQpyZXMucGNhJHZhciRjb250cmliCmBgYAoKCgoqKjgpIEdpdmUgdGhlIFIgb2JqZWN0IHdpdGggdGhlIHR3byBwcmluY2lwYWwgY29tcG9uZW50cyB3aGljaCBhcmUgdGhlIHN5bnRoZXRpYyB2YXJpYWJsZXMgdGhlIG1vc3QgY29ycmVsYXRlZCB0byBhbGwgdGhlIHZhcmlhYmxlcy4qKgoKVGhlc2UgYXJlIGZvdW5kIGluIHRoZSBjb2RlIGJlbG93IC0gCgpgYGB7cn0KcmVzLnBjYSR2YXIkY29vcmRbLDE6Ml0KYGBgCgoqKjkpIFBDQSBpcyBvZnRlbiB1c2VkIGFzIGEgcHJlLXByb2Nlc3Npbmcgc3RlcCBiZWZvcmUgYXBwbHlpbmcgYSBjbHVzdGVyaW5nIGFsZ29yaXRobSwgZXhwbGFpbiB0aGUgcmF0aW9uYWxlIG9mIHRoaXMgYXBwcm9hY2ggYW5kIGhvdyBtYW55IGNvbXBvbmVudHMgayB5b3Uga2VlcC4qKgoKV2UgY2hvc2UgdGhlIG1heGltdW0gbnVtYmVyIG9mIGNvbXBvbmVudHMgYXMgdG8gbm90IHRvIGxvc2UgYW55IHNpZ25pZmljYW50IGluZm9ybWF0aW9uIHdoaWxzdCBkaXNjYXJkaW5nIHRoZSBjb21wb25lbnRzIHRoYXQgY2FuIGJlIGNvbnNpZGVyZWQgYXMgbm9pc2UuIENvbnNlcXVlbnRseSwgd2Uga2VlcCB0aGUgbnVtYmVyIG9mIGRpbWVuc2lvbnMgc3VjaCB0aGF0IHdlIGtlZXAgOTUlIG9mIHRoZSBpbmVydGlhIGluIFBDQSwgd2hpY2ggaXMgZXF1aXZhbGVudCB0byAzIGNvbXBvbmVudHMgaW4gb3VyIGFuYWx5c2lzLiAKCioqMTApIFBlcmZvbXMgYSBrbWVhbnMgYWxnb3JpdGhtIG9uIHRoZSBzZWxlY3RlZCBrIHByaW5jaXBhbCBjb21wb25lbnRzIG9mIFBDQS4gSG93IG1hbnkgY2x1c3RlciBhcmUgeW91IGtlZXBpbmc/IEp1c3RpZnkuKioKCldlIHVzIHRoZSBFbGJvdyBtZXRob2QgYW5kIGxvb2sgYXQgdGhlIGtuZWUgdG8gZGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgd2Uga2VlcCAzIGNsdXN0ZXJzIGhlcmUuIAoKUmVjYWxsIHRoYXQsIHRoZSBiYXNpYyBpZGVhIGJlaGluZCBrLW1lYW5zIGNsdXN0ZXJpbmcgaXMgdG8gZGVmaW5lIGNsdXN0ZXJzIHN1Y2ggdGhhdCB0aGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgdmFyaWF0aW9uIGlzIG1pbmltaXplZC4gVGhlIHRvdGFsIFdTUyBtZWFzdXJlcyB0aGUgY29tcGFjdG5lc3Mgb2YgdGhlIGNsdXN0ZXJpbmcgYW5kIHdlIHdhbnQgaXQgdG8gYmUgYXMgc21hbGwgYXMgcG9zc2libGUuIFRoZSBFbGJvdyBtZXRob2QgbG9va3MgYXQgdGhlIHRvdGFsIFdTUyBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBudW1iZXIgb2YgY2x1c3RlcnM6IG9uZSBzaG91bGQgY2hvb3NlIGEgbnVtYmVyIG9mIGNsdXN0ZXJzIHNvIHRoYXQgdGhlIG1hcmdpbmFsIGNsdXN0ZXIgZG9lc27igJl0IGltcHJvdmUgV1NTLiBIZXJlIHRoaXMgdmFsdWUgaXMgMy4gCgpgYGB7cn0KcmVzLnBjYV8zIDwtIFBDQShyYXdfZGF0YSxxdWFsaS5zdXAgPSBjKDEsNSw2LDcsOSkscXVhbnRpLnN1cCA9IDEwLCBuY3A9MywgZ3JhcGggPSBGQUxTRSwgc2NhbGUgPSBUUlVFKSAKZnZpel9uYmNsdXN0KHJlcy5wY2FfMyRpbmQkY29vcmQsIGttZWFucywgbWV0aG9kID0gIndzcyIpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMywgbGluZXR5cGUgPSAyKQprX2NsdXN0ZXIgPC0ga21lYW5zKHJlcy5wY2FfMyRpbmQkY29vcmQsIDMpCmtfbWVhbl9kYXRhIDwtIGFzLm1hdHJpeChyZXMucGNhXzMkaW5kJGNvb3JkKQpwbG90KGtfbWVhbl9kYXRhLCBjb2wgPSBrX2NsdXN0ZXIkY2x1c3RlciwgcGNoID0gMjAsIGZyYW1lID0gRkFMU0UsICBtYWluID0gIkstbWVhbnMiKQpgYGAKCioqMTEpIFBlcmZvcm0gYW4gQUhDIG9uIHRoZSBzZWxlY3RlZCBrIHByaW5jaXBhbCBjb21wb25lbnRzIG9mIFBDQS4qKgoKQmVsb3cgd2UgcGVyZm9ybSBhbiBBSEMgb24gdGhlIHNlbGVjdCAzIHByaW5jaXBhbCBjb21wb25lbnRzIG9mIFBDQS4gCgpgYGB7cn0KCnJlcy5wY2FfMyA8LSBQQ0EocmF3X2RhdGEscXVhbGkuc3VwID0gYygxLDUsNiw3LDkpLHF1YW50aS5zdXAgPSAxMCwgbmNwPTMsIGdyYXBoID0gRkFMU0UsIHNjYWxlID0gVFJVRSkgCnJlcy5oY3BjIDwtIEhDUEMocmVzLnBjYV8zLCBuYi5jbHVzdCA9IC0xLCBncmFwaCA9IEZBTFNFKQoKcGxvdC5IQ1BDKHJlcy5oY3BjLCBjaG9pY2UgPSAnbWFwJywgZHJhdy50cmVlID0gRkFMU0UsIHNlbGVjdCA9ICJjb3MyIDUiLCB0aXRsZSA9ICdGYWN0b3IgTWFwJykKcGxvdC5IQ1BDKHJlcy5oY3BjLCBjaG9pY2UgPSAnYmFyJywgZHJhdy50cmVlID0gRkFMU0UsIHRpdGxlID0gJ0luZXJ0aWEgR2FpbicpCnBsb3QuSENQQyhyZXMuaGNwYywgY2hvaWNlID0gJzNELm1hcCcsIGRyYXcudHJlZSA9IEZBTFNFLCB0aXRsZSA9ICdIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyBvbiB0aGUgRmFjdG9yIE1hcCcsIGFuZ2xlPTYwKQpwbG90LkhDUEMocmVzLmhjcGMsIGNob2ljZSA9ICd0cmVlJywgZHJhdy50cmVlID0gRkFMU0UsIHRpdGxlID0gJ0hpZXJhcmNoaWNhbCBDbHVzdGVyaW5nJykKYGBgCgoKKioxMikgQ29tbWVudHMgdGhlIHJlc3VsdHMgYW5kIGRlc2NyaWJlIHByZWNpc2VseSBvbmUgY2x1c3Rlci4qKgoKVGhlIGNsYXNzZmljYXRpb24gbWFkZSBvbiBpbmRpdmlkdWFscyByZXZlYWxzIDMgY2x1c3RlcnMuIAoKV2Ugbm93IGFpbSBhdCBkZXNjcmliaW5nIHRoZSBjbHVzdGVycy4gCgpGb3IgZWFjaCBxdWFudGl0YXRpdmUgdmFyaWFibGUsIHdlIGJ1aWxkIGFuIGFuYWx5c2lzIG9mIHZhcmlhbmNlIG1vZGVsIGJldHdlZW4gdGhlIHF1YW50aXRhdGl2ZSB2YXJpYWJsZSBhbmQgdGhlIGNsYXNzIHZhcmlhYmxlLCBkbyBhIEZpc2hlciB0ZXN0IHRvIGRldGVjdCBjbGFzcyBlZmZlY3QgYW5kIHNvcnQgdGhlIHZhcmlhYmxlcyBieSBpbmNyZWFzaW5nIHAtdmFsdWUuIFdlIGNhbiBkaXJlY3RseSBmaW5kIHRoZSByZXN1bHRzIGluIHRoZSBmb2xsb3dpbmcgb2JqZWN0czogCgpgYGB7cn0KcmVzLmhjcGMkZGVzYy52YXIkcXVhbnRpLnZhcgpgYGAKCkZvciBoaWdoIHZhbHVlcyBvZiBFdGEyLCBhcyBzZWVuIGluIHRoZSBsZWN0dXJlLCB0aGVyZSBpcyBhIHJlbGF0aW9uIGJldHdlZW4gdGhlIGNsdXN0ZXJpbmcgYW5kIHRoZSBxdWFudGl0YXRpdmUgdmFyaWFibGUuIFRodXMgd2UgY2FuIHNheSB0aGlzIGlzIHRoZSBjYXNlIGZvciBMZW5ndGgsIERpYW1ldGVyLCB3ZWlnaHQgYW5kIE1hdHVyZS5Wb2x1bWUuIAoKV2Ugbm93IHR1cm4gb3VyIGF0dGVudGlvbiB0byBkZXNjcmliaW5nIHRoZSBvYnNlcnZhdGlvbnMgaW4gYSBjbHVzdGVyIHVzaW5nIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuIAoKYGBge3J9CnJlcy5oY3BjJGRlc2MudmFyJHF1YW50aQpgYGAKCkJlbG93LCB3ZSBhbHNvIGlsbHVzdHJhdGUgY2x1c3RlcnMgYnkgY29uc2lkZXJpbmcgcGFyYWdvbnMgYW5kIHNwZWNpZmljIGluZGl2dWFscy4gCgpgYGB7cn0KcmVzLmhjcGMkZGVzYy5pbmQKYGBgCgpGaW5hbGx5LCB3ZSBhbHNvIGxvb2sgYXQgdGhlIGNhdGVlZ29yaWNhbCB2YXJpYWJsZXMgZm9yIGNsdWVzIG9uIGNsdXN0ZXJzOiAKCmBgYHtyfQpyZXMuaGNwYyRkZXNjLnZhciR0ZXN0LmNoaTIKYGBgCgpUaGUgdmFyaWFibGUgSW1wZXJtZWFiaWxpdHkgYW5kIFJhdy5NYXRlcmlhbHMgc2VlbSB0byBiZSB2ZXJ5IHNpZ2luaWZpY2F0aXZlbHkgbGlua2VkIHRvIHBhcnRpb25pbmcsIHdpdGggU2hhcGUgYW5kIFN1cHBsaWVyIGFsc28gc2lnbmlmaWNhbnQgdG8gYSBjZXJ0YWludCBleHRlbnQuIEZvciBlYWNoIHF1YWxpdGF0aXZlIHZhcmlhYmxlLCB3ZSBwZXJmb3JtIGEgY2hpMiB0ZXN0IGJldHdlZW4gaXQgYW5kIHRoZSBjbGFzcyB2YXJpYWJsZSBhbmQgdGhlbiBzb3J0IHRoZSB2YXJpYWJsZXMgYnkgaW5jcmVhc2luZyBwLXZhbHVlLgoKYGBge3J9CnJlcy5oY3BjJGRlc2MudmFyJGNhdGVnb3J5CmBgYAoKV2UgY2FuIGNvbmZpcm0gdGhhdCB0aGUgdGhlc2UgcXVhbGl0YXRpdmUgdmFyaWFibGVzIHBsYXkgYSBzaWduaWZpY2FudCByb2xlIGluIGVhY2ggb2YgdGhlIGNsdXN0ZXJzIGluZGl2aWR1YWxseSBhcyB3ZWxsLiAKCgpXZSBsaW5rIHRoaXMgYWxsIGJhY2sgdG8gb3VyIFBDQSAtIAoKYGBge3J9CnJlcy5oY3BjJGRlc2MuYXhlcwpgYGAKClRoZSBpbmZvcm1hdGlvbiBhYm92ZSBzdWdnZXN0cyB0aGVyZSBpcyBzdHJvbmcgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBjbHVzdGVycyBhbmQgdGhlIGRpbWVuc2lvbnM6CmNsdXN0ZXIgMSAtIGhhdmUgY29vcmRpbmF0ZXMgc2lnbmljYW50bHkgc21hbGxlciB0byAwIGluIHRoZSBmaXJzdCBkaW1lbnNpb24gYW5kIHNlY29uZCBkaW1lbnNpb24gYnV0IGNvb3JkaW5hdGVzIHNpZ25maWNhbnRseSBncmVhdGVyIHRoYW4gMCBpbiB0aGUgdGhpcmQgZGltZW5zaW9uLgpjbHVzdGVyIDIgLSBoYXZlIGNvb3JkaW5hdGVzIHNpZ25pY2FudGx5IHNtYWxsZXIgdG8gMCBpbiB0aGUgZmlyc3QgZGltZW5zaW9uIGFuZCB0aGlyZCBkaW1lbnNpb24gYnV0IGNvb3JkaW5hdGVzIHNpZ25maWNhbnRseSBncmVhdGVyIHRoYW4gMCBpbiB0aGUgc2Vjb25kIGRpbWVuc2lvbi4KY2x1c3RlciAzIC0gaGF2ZSBjb29yZGluYXRlcyBzaWduaWNhbnRseSBncmVhdGVyIHRvIDAgaW4gdGhlIGZpcnN0IGRpbWVuc2lvbi4gCgpUaGlzIGlzIGV4dHJlbWVseSBpbnNpZ2h0ZnVsIHdoZW4gd2UgY29uc2lkZXIgdGhlIGFuYWx5c2lzIHdlIGhhdmUgcGVyZm9ybWVkIHByZXZpb3VzbHkuIAoKSW4gZW5nbGlzaCwgdGhlIHN1bW1hcnkgb2YgdGhlIGFib3ZlIGFuYWx5c2lzIGNhbiBiZSB3cml0dGVuIGFzOiAKClRoZSBjbHVzdGVyIDEgaXMgbWFkZSBvZiBpbmRpdmlkdWFscyBzdWNoIGFzIHNwZWNpZmljIGluZGl2aWR1YWwgNzcgc2hhcmluZyA6Ci0gaGlnaCB2YWx1ZXMgZm9yIHRoZSB2YXJpYWJsZSBNYXR1cmUuVm9sdW1lLiAKLSBsb3cgdmFsdWVzIGZvciB0aGUgdmFyaWFibGVzIG5iLm9mLnBpZWNlcywgUHJpY2UsIHdlaWdodCwgTGVuZ3RoIGFuZCBEaWFtZXRlciAodmFyaWFibGVzIGFyZSBzb3J0ZWQgZnJvbSB0aGUgd2Vha2VzdCkuCgpUaGUgY2x1c3RlciAyIGlzIG1hZGUgb2YgaW5kaXZpZHVhbHMgc2hhcmluZyBzdWNoIGFzIHNwZWNpZmljIGluZGl2aWR1YWwgMTgzIHNoYXJpbmc6Ci0gaGlnaCB2YWx1ZXMgZm9yIHRoZSB2YXJpYWJsZSBuYi5vZi5waWVjZXMuIAotIGxvdyB2YWx1ZXMgZm9yIHRoZSB2YXJpYWJsZXMgTWF0dXJlLlZvbHVtZSwgRGlhbWV0ZXIsIExlbmd0aCwgd2VpZ2h0IGFuZCBQcmljZSAodmFyaWFibGVzIGFyZSBzb3J0ZWQgZnJvbSB0aGUgd2Vha2VzdCkuCgpUaGUgY2x1c3RlciAzIGlzIG1hZGUgb2YgaW5kaXZpZHVhbHMgc3VjaCBhcyBzcGVjaWZpYyBpbmRpdmlkdWFsIDg0IHNoYXJpbmcgOgotIGhpZ2ggdmFsdWVzIGZvciB0aGUgdmFyaWFibGVzIExlbmd0aCwgRGlhbWV0ZXIsIHdlaWdodCBhbmQgUHJpY2UgKHZhcmlhYmxlcyBhcmUgc29ydGVkIGZyb20gdGhlIHN0cm9uZ2VzdCkuCi0gbG93IHZhbHVlcyBmb3IgdGhlIHZhcmlhYmxlcyBuYi5vZi5waWVjZXMgYW5kIE1hdHVyZS5Wb2x1bWUgKHZhcmlhYmxlcyBhcmUgc29ydGVkIGZyb20gdGhlIHdlYWtlc3QpLgoKCioxMykgSWYgc29tZW9uZSBhc2sgeW91IHdoeSB5b3UgaGF2ZSBzZWxlY3RlZCBrIGNvbXBvbmVudHMgYW5kIG5vdCBrICsgMSBvciBrIOKIkiAxLCB3aGF0IGlzIHlvdXIgYW5zd2VyPyAoY291bGQgeW91IHN1Z2dlc3QgYSBzdHJhdGVneSB0byBhc3Nlc3MgdGhlIHN0YWJpbGl0eSBvZiB0aGUgYXBwcm9hY2g/IC0gYXJlIHRoZXJlIG1hbnkgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgY2x1c3RlcmluZyBvYnRhaW5lZCBvbiBrIGNvbXBvbmVudHMgb3Igb24gdGhlIGluaXRpYWwgZGF0YSkqCgpJbiB0aGUgSHVzc29uIHRleHRib29rLCBpdCBpcyBzdWdnZXN0ZWQgdG8gdGVzdCB0aGUgcGVyY2VudGFnZSBpbnRlcnRpYSBleHByZXNzZWQgYnkgYSBjb21wb25lbnQgYW5kIHRoZW4gdGhlIHBlcmNlbnRhZ2Ugb2YgaW5lcnRpYSBleHByZXNzZWQgYnkgdGhlIGZpcnN0IHBsYW5lLiBUaGUgbWV0aG9kIHNpbXVsYXRlcyAxMCwwMDAgZGF0YXNldHMgZm9yIDE5MSBpbmRpdmlkdWFsIHNhbmQgMTEgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgaW5kZXBlbmRlbnQgdmFyaWFibGVzIC0gdGhpcyBhbGxvdyB1cyB0byBtYWtlIGEgY29tcGFyaXNvbiBieSB0YWtpbmcgaW50byBhY2NvdW50IHRoZSBudW1iZXIgb2YgYWN0aXZlIGluZGl2aWR1YWxzIGFuZCBhY3RpdmUgdmFyaWFibGVzLiBJdCB0aGVuIGNvbmR1Y3QgYSBzdGFuZGFyZGlzZWQgUENBIGZvciBlYWNoIGRhdGFzZXQgYW5kIGNhbGN1bGF0ZXMgdGhlIHBlcmNlbnRhZ2Ugb2YgaW5lcnRpYSBleHBsYWluZWQgYnkgb25lIGNvbXBvbmVudCBhbmQgdGhlIHRoYXQgZXhwcmVzc2VkIGJ5IG9uZSBwbGFuZS4gVGhlbiwgdGhlIG1ldGhvZCBkZWZpbmVzIHRoZSBxdWFudGlsZSAwLjk1IG9mIHRoZSAxMCwwMDAgcGVyY2VudGFnZXMgb2YgaW5lcnRpYSBvZiB0aGUgZmlyc3QgY29tcG9uZW50IChhbmQgdGhlIGZpcnN0IHBsYW5lLCByZXNwZWN0aXZlbHkpIG9idGFpbmVkLiBDb21wYXJpbmcgdGhlIHBlcmNlbnRhZ2Ugb2YgaW5lcnRpYSBvZiBhIGNvbXBvbmVudCBvciBwbGFuZSB3aXRoIHRoZSBhc3NvY2lhdGVkIHZhbHVlIGluIHRoZSB0YWJsZSB0aGVuIGFtb3VudHMgdG8gdGVzdGluZyB0aGUgbnVsbCBoeXBvdGhlc2lzIEgwOiB0aGUgcGVyY2VudGFnZSBvZiBpbmVydGlhIGV4cGxhaW5lZCBieSB0aGUgZmlyc3QgY29tcG9uZW50IChhbmQgdGhlIGZpcnN0IHBsYW5lLCByZXNwZWN0aXZlbHkpIGlzIG5vdCBzaWduaWZpY2FudGx5IGdyZWF0ZXIgdGhhbiB0aGF0IG9idGFpbmVkIHdpdGggdGhlIChub3JtYWxseSBkaXN0cmlidXRlZCkgaW5kZXBlbmRlbnQgZGF0YS4KClRoZSBmaXJzdCB0d28gZGltZW5zaW9ucyBvZiBQQ0EgZXhwcmVzcyA4My40OCUgb2YgdGhlIHRvdGFsIGRhdGFzZXQgaW5lcnRpYSAtIHRoaXMgdmFsdWUgaXMgc3Ryb25nbHkgZ3JlYXRlciB0aGFuIHRoZSByZWZlcmVuY2UgdmFsdWUgdGhhdCBlcXVhbHMgNDguMjIlIHVzaW5nIHRoZSBtZXRob2RvbG9neSBhYm92ZS4gVGhpcyB3b3VsZCBzdWdnZXN0IGl0IGlzIHByb2JhYmx5IG5vdCB1c2VmdWwgdG8gaW50ZXJwcmV0IHRoZSBuZXh0IGRpbWVuc2lvbnMgYW5kIHdvdWxkIGJlIHRoZSBiYXNpcyBvZiBrKzEsIGstMSBkZWNpc2lvbnMuIAoKSG93ZXZlciwgdGhpcyBtZXRob2QgbWlnaHQgZGlzY2FyZCBzaWduaWZpY2FudCBpbmZvcm1hdGlvbi4gQ29uc2VxdWVudGx5LCB3ZSB3aWxsIHVzZSBhIG1ldGhvZG9sb2d5IHNpbWlsYXIgdG8gdGhlIEVsYm93IG1ldGhvZCAtIHdlIHBlcmZvcm0gSENQQyB3aXRoIGstMSwgayBhbmQgaysxIGNvbXBvbmVudHMgYW5kIGNvbXBhcmUgYW5kIGNvbnRyYXN0IHRoZSByZXN1bHRzLiBGb3IgZXhhbXBsZSwgd2UgY291bGQgbG9vayBhdCB0aGUgcXVhbGl0eSBhbmQgc2lnbmlmaWNhbmNlIGdhaW4gdGhhdCBlYWNoIFBDQSBvZmZlcnMgd2hlbiBhbmF5bHlzaW5nIHRoZSBjbHVzdGVycy4gSGVyZSB0aGlzIGNvcnJlc3BvbmRzIHRvIDIsMyBhbmQgNCBjb21wb25lbnRzLiBXZSBub3RpY2UgdGhhdCBmb3IgYWxsIDMsIEhDUEMgd2lsbCBjdXQgdGhlIHRyZWUgYXQgMyBjbHVzdGVycyAodmlzdWFsbHkgdGhlIHRocmVlIHBsb3RzIGFyZSBzaW1pbGFyKS4gSG93ZXZlciwgYWZ0ZXIgYW5hbHlzaW5nIHRoZSBwLXZhbHVlcyB3ZSBub3RpY2UgdGhhdCB0aGVyZSBpcyBhbiBpbXByb3ZlbWVudCBmcm9tIGdvaW5nIHRvIDIgdG8gMyB3aGVyZWFzIGZyb20gcC12YWx1ZXMgZm9yIDMgYW5kIDQgYXJlIHZlcnkgc2ltaWxhci4gCgoKYGBge3J9CnJlcy5wY2EgPC0gUENBKHJhd19kYXRhLHF1YWxpLnN1cCA9IGMoMSw1LDYsNyw5KSxxdWFudGkuc3VwID0gMTAsbmNwPTQsIHNjYWxlPVRSVUUsIGdyYXBoID0gRkFMU0UpCnJlcy5oY3BjIDwtIEhDUEMocmVzLnBjYSwgbmIuY2x1c3QgPSAtMSwgZ3JhcGggPSBGQUxTRSkKcmVzLmhjcGMkZGVzYy52YXIKcGxvdC5IQ1BDKHJlcy5oY3BjLCBjaG9pY2UgPSAnbWFwJywgZHJhdy50cmVlID0gRkFMU0UsIHNlbGVjdCA9ICJjb3MyIDUiLCB0aXRsZSA9ICdGYWN0b3IgTWFwIGs9NCcpCgoKcmVzLnBjYSA8LSBQQ0EocmF3X2RhdGEscXVhbGkuc3VwID0gYygxLDUsNiw3LDkpLHF1YW50aS5zdXAgPSAxMCwgbmNwPTMsIHNjYWxlID0gVFJVRSwgZ3JhcGggPSBGQUxTRSkKcmVzLmhjcGMgPC0gSENQQyhyZXMucGNhLCBuYi5jbHVzdCA9IC0xLGdyYXBoID0gRkFMU0UpCnJlcy5oY3BjJGRlc2MudmFyCnBsb3QuSENQQyhyZXMuaGNwYywgY2hvaWNlID0gJ21hcCcsIGRyYXcudHJlZSA9IEZBTFNFLCBzZWxlY3QgPSAiY29zMiA1IiwgdGl0bGUgPSAnRmFjdG9yIE1hcCBrPTMnKQoKcmVzLnBjYSA8LSBQQ0EocmF3X2RhdGEscXVhbGkuc3VwID0gYygxLDUsNiw3LDkpLHF1YW50aS5zdXAgPSAxMCxuY3A9Miwgc2NhbGUgPSBUUlVFLGdyYXBoID0gRkFMU0UpCnJlcy5oY3BjIDwtIEhDUEMocmVzLnBjYSwgbmIuY2x1c3QgPSAtMSwgZ3JhcGggPSBGQUxTRSkKcmVzLmhjcGMkZGVzYy52YXIKcGxvdC5IQ1BDKHJlcy5oY3BjLCBjaG9pY2UgPSAnbWFwJywgZHJhdy50cmVlID0gRkFMU0UsIHNlbGVjdCA9ICJjb3MyIDUiLCB0aXRsZSA9ICdGYWN0b3IgTWFwIGs9MicpCgpgYGAKCgoKKioxNCkgVGhlIG1ldGhvZG9sb2d5IHRoYXQgeW91IGhhdmUgdXNlZCB0byBkZXNjcmliZSBjbHVzdGVycyBjYW4gYWxzbyBiZSB1c2VkIHRvIGRlc2NyaWJlIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUsIGZvciBpbnN0YW5jZSB0aGUgc3VwcGxpZXIuIFVzZSB0aGUgZnVuY3Rpb24gY2F0ZGVzKGRhdGEsIG51bS52YXI9MSkgYW5kIGV4cGxhaW4gaG93IHRoaXMgaW5mb3JtYXRpb24gY2FuIGJlIHVzZWZ1bCBmb3IgdGhlIGNvbXBhbnkuKioKCkZyb20gdGhlIGNoaSBzcXVhcmVkIHRlc3RzLCB3ZSBoYXZlIGFuIGluZGljYXRpb24gdGhhdCBSYXcuTWF0ZXJpYWwgYW5kIEltcGVybWVhYmlsaXR5IGhhdmUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIHdpdGggcmVnYXJkIHRvIHRoZSBkaWZmZXJlbnQgc3VwcGxpZXJzIGdyb3Vwcy4gSW4gcGFydGljdWxhciwgd2UgaGF2ZSB0aGUgZm9sbG93aW5nIGNoYXJhY3RlcmlzdGljcyBmb3IgZWFjaCBzdXBwbGllciAoZnJvbSBtb3N0IHNpZ25pZmljYW50IHRvIGxlYXN0IHNpZ25pZmljYW50KTogClN1cHBsaWVyIEE6IFBTLCBUeXBlIDIsIFR5cGUgMSwgU2hhcGUgMiBhbmQgQUJTIApTdXBwbGllciBCOiBTaGFwZSAyLCBBQlMsIFBTIDsgbmIub2YucGllY2VzClN1cHBsaWVyIEM6IFBQOyBuYi5vZi5waWVjZXMKCkdpdmVuIHRoZXNlIGFyZSBjYXRlZ29yaWVzIHRoYXQgYmVzdCBkaXN0aW5ndWlzaCB0aGUgZGlmZmVyZW50IHN1cHBsaWVycyBmcm9tIG9uZSBhbm90aGVyIGFuZCBvZmZlciBzb21lIGNoYXJhY3RlcmlzdGljcywgdGhlIGNvc21ldGljcyBjb21wYW55IGNvdWxkIHVzZSB0aGlzIHRvIGJlbmNobWFyayB0aGVtLCB3aGljaCBjb3VsZCBiZSBleHRyZW1lbHkgdXNlZnVsLiBjYXRkZXMgY2FuIGJlIHVzZWQgb24gb3RoZXIgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGZvciBzdWNoIGluc2lnaHRzLiAKCgpgYGB7cn0KY2F0ZGVzX3N1cHBsaWVyPC0gY2F0ZGVzKHJhd19kYXRhLCBudW0udmFyPTEpCmNhdGRlc19zdXBwbGllcgpwbG90KGNhdGRlc19zdXBwbGllciwgY29sID0gImRhcmtibHVlIikKYGBgCgoKKioxNSkgVG8gc2ltdWx0YW5lb3VzbHkgdGFrZSBpbnRvIGFjY291bnQgcXVhbnRpdGF0aXZlIGFuZCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgaW4gdGhlIGNsdXN0ZXJpbmcgeW91IHNob3VsZCB1c2UgdGhlIEhDUEMgZnVuY3Rpb24gb24gdGhlIHJlc3VsdHMgb2YgdGhlIEZBTUQgb25lcy4gRkFNRCBzdGFuZHMgZm9yIEZhY3RvcmlhbCBBbmFseXNpcyBvZiBNaXhlZCBEYXRhIGFuZCBpcyBhIFBDQSBkZWRpY2F0ZWQgdG8gbWl4ZWQgZGF0YS4gRXhwbGFpbiB3aGF0IHdpbGwgYmUgdGhlIGltcGFjdHMgb2Ygc3VjaCBhbiBhbmFseXNpcyBvbiB0aGUgcmVzdWx0cz8qKgoKSW4gRkFNRCwgdGhlIGluZmx1ZW5jZSBvZiBib3RoIGNvbnRpbm91cyBhbmQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGluIHRoZSBhbmFseXNpcyBpcyBiYWxhbmNlZC4gSXQgbWVhbnMgdGhhdCBib3RoIHZhcmlhYmxlcyBhcmUgb24gYSBlcXVhbCBmb290IHRvIGRldGVybWluZSB0aGUgZGltZW5zaW9ucyBvZiB2YXJpYWJpbGl0eS4gVGh1cyBGQU1EIHRodXMgY2hhbmdlcyB0aGUgY29ycmVsYXRpb24gYW5kIHZhcmlhbmNlIHN0cnVjdHVyZXMgb2YgdGhlIHByZXZpb3VzIGFuYWx5c2lzLiBUaGUgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggY29tcG9uZW50IHJlZHVjZXMgc3VjaCB0aGF0IHdlIHdvdWxkIG5lZWQgMTAgY29tcG9uZW50cyB0byBleHBsYWluIDk1JSBvZiB0b3RhbCB2YXJpYW5jZS4gCgpMb29raW5nIGF0IHRoZSBncmFwaHMsIHRoZSBQcmljZSBjb250aW51ZXMgdG8gYmUgc3Ryb25nbHkgY29ycmVsYXRlZCB0byB0aGUgZmlyc3QgY29tcG9uZW50LCBhcyBhcmUgd2VpZ2h0IExlbmd0aCwgRGlhbWV0ZXIsIEltcGVybWVhYmlsaXR5IGFuZCBSYXcuTWF0ZXJpYWxzLiBTaGFwZSBpcyBhbHNvIHNvbWV3aGF0IHdlbGwgY29ycmVsYXRlZCB0byB0aGUgZmlyc3QgY29tcG9uZW50IGFuZCB0aHVzIHRvIFByaWNlLiAgSG93ZXZlciwgRmluaXNoaW5nLCBNYXR1cmUuVm9sdW1lIGFyZSBub3Qgd2VsbCByZXByZXNlbnRlZCBvbiB0aGUgbWFwIGFuZCBTdXBwbGllciBhbmQgbmIub2YucGllY2VzIGFyZSBjb3JyZWxhdGVkIHRvIHRoZSBzZWNvbmQgY29tcG9uZW50IGFuZCB0aHVzIHVuY29ycmVsYXRlZCB0byBQcmljZS4KCkZ1cnRoZXJtb3JlLCBnaXZlbiBtYW55IG1vcmUgY29tcG9uZW50cyBhcmUgdGFrZW4gaW50byBhY2NvdW50IHRvIGVuc3VyZSBpbmZvcm1hdGlvbiBpcyBub3QgbG9zdCwgdGhlIGNsdXN0ZXJpbmcgaXMgYWxzbyBhZmZlY3RlZCBhbmQgc2VlbXMgbm93IGxlc3MgaW5mb3JtYXRpdmUgdGhhbiBwcmV2aW91c2x5LiAKCmBgYHtyfQoKcmVzLmZhbWQgPC0gRkFNRCAocmF3X2RhdGEsIHN1cC52YXIgPSBjKDEwKSwgbmNwID0gMTAgLCBheGVzID0gYygxLDIpLCByb3cudyA9IE5VTEwsIHRhYi5jb21wID0gTlVMTCwgZ3JhcGggPSBGQUxTRSkKcmVzLmZhbWQkZWlnCgpmdml6X2ZhbWRfdmFyKHJlcy5mYW1kLCByZXBlbCA9IFRSVUUpCmZ2aXpfZmFtZF92YXIocmVzLmZhbWQsICJxdWFudGkudmFyIiwgY29sLnZhciA9ICJjb3MyIiwgZ3JhZGllbnQuY29scyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiksIHJlcGVsID0gVFJVRSkKZnZpel9mYW1kX3ZhcihyZXMuZmFtZCwgInF1YWxpLnZhciIsIGNvbC52YXIgPSAiY29zMiIsIGdyYWRpZW50LmNvbHMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIpKQpmdml6X2ZhbWRfaW5kKHJlcy5mYW1kLCBjb2wuaW5kPSJjb3MyIiwgbGFiZWw9YygicXVhbGkiKSwgZ2VvbSA9ICJwb2ludCIsIGhhYmlsbGFnZSA9ICJub25lIikgKyBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93PSJsaWdodGJsdWUiLCBtaWQ9ImJsdWUiLCBoaWdoPSJkYXJrYmx1ZSIsIG1pZHBvaW50PTAuNikgKyB0aGVtZV9taW5pbWFsKCkgCgpyZXMuaGNwYy5mYW1kIDwtIEhDUEMocmVzLmZhbWQsIG5iLmNsdXN0ID0gLTEsIGdyYXBoID0gRkFMU0UpCnBsb3QuSENQQyhyZXMuaGNwYy5mYW1kLCBjaG9pY2UgPSAnbWFwJywgZHJhdy50cmVlID0gRkFMU0UsIHNlbGVjdCA9IGMoMSwxMCksIHRpdGxlID0gJycpCgoKCmBgYAoKKioxNikgUGVyZm9ybSBhIG1vZGVsIHRvIHByZWRpY3QgdGhlIFByaWNlLiBFeHBsYWluIGhvdyB0aGUgcHJldmlvdXMgYW5hbHlzaXMgY2FuIGhlbHAgeW91IHRvIGludGVycHJldCB0aGUgcmVzdWx0cy4qKgoKV2UgZmlyc3Qgc3BsaXQgb3VyIGRhdHRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cy4gSGVyZSB3ZSB3aWxsIGdlbmVyYXRlIGEgcmFuZG9tIHNhbXBsZSBmcm9tIHRoZSBkYXRhIHdoaWNoIHdpbGwgY29uc2lzdCBvZiA3MCUgdG8gOTAlIG9mIHRoZSByb3dzIGluIHRoZSBvcmlnaW5hbCBkYXRhIHRvIHRyYWluIHRoZSBtb2RlbCBvbiBhbmQgd2lsbCB0ZXN0IG9uIHRoZSByZW1haW5pbmcgcm93cy4gCgpUaGUgdmFyaWFibGVzIHdlIGNob3NlIGZvciBvdXIgbGluZWFyIG1vZGVsIHdpbGwgYmUgdGhvc2Ugd2hpY2ggd2UgaGF2ZSBzZWVuIGFyZSBtb3N0IHNpZ2luaWZpY2FudCBmb3IgUHJpY2UgZnJvbSBvdXIgYW5hbHlzaXMgLSBuYW1lbHk6IAotIFByaWNlLCBEaWFtZXRlciwgTGVuZ3RoIAotIEltcGVybWVhYmlsaXR5LCBSYXcuTWF0ZXJpYWxzLCBTaGFwZQoKTm90aWNlIHRodCB0aGUgUiBzcXVhcmVkIG9mIHRoZSByZWdyZXNzaW9uIGlzIHNhdGlzZmFjdG9yeS4gCgpgYGB7cn0KdHJhaW5pbmdfc2V0IDwtIHNhbXBsZV9uKHJhd19kYXRhLCByb3VuZCgocnVuaWYoMSxtaW49MC43MCxtYXg9MC45MCkgKiBkaW0ocmF3X2RhdGEpWzFdKSwwKSApCmRpbSh0cmFpbmluZ19zZXQpIAp0ZXN0X3NldCA8LSByYXdfZGF0YVshKHJhd19kYXRhJExlbmd0aCAlaW4lIHRyYWluaW5nX3NldCRMZW5ndGgpLF0KZGltKHRlc3Rfc2V0KSAKYXNzZXJ0dGhhdDo6YXJlX2VxdWFsKGRpbSh0cmFpbmluZ19zZXQpWzFdKyBkaW0odGVzdF9zZXQpWzFdICxkaW0ocmF3X2RhdGEpWzFdKQoKbG1fZml0IDwtIGxtKFByaWNlIH4gRGlhbWV0ZXIgKyBMZW5ndGggKyB3ZWlnaHQgKyBSYXcuTWF0ZXJpYWwgKyBJbXBlcm1lYWJpbGl0eSArIFNoYXBlLCBkYXRhPSB0cmFpbmluZ19zZXQpCmxtX3ByZWRpY3Rpb24gPC0gcHJlZGljdC5sbShsbV9maXQsIHRlc3Rfc2V0KQoKc3VtbWFyeShsbV9maXQpCmBgYAoKV2Ugbm93IHdpc2ggdG8gYXNzZXNzIHRoZSB0aGUgbGluZWFyIG1vZGVsLiBPYnZpb3VzbHksIHRoZSBxdWFsaXR5IG9mIHRoZSByZXN1bHRzIGRlcGVuZCBvbiBzaXplIGFuZCB0aGUgcXVhbGl0eSBvZiB0aGUgdHJhaW5pbmcgc2V0LCBob3dldmVyIHdlIGNhbiBzZWUgdGhhdCBpbiBnZW5lcmFsLCBpdCBwZXJmb3JtcyBwb29ybHkgZm9yIHByaWNlcyBpbiB0aGUgdGFpbHMgb2YgdGhlIHByaWNlIGRpc3RyaWJ1dGlvbi4gV2UgY2FuIHZpZXcgdGhlIHF1YW50aWxlcyB0byBoYXZlIGFuIGlkZWEgb2YgdGhlIHByZWNpc2lvbiBvZiB0aGUgcHJlZGljdGlvbi4gTm90ZSB3ZSBjYW4gbWFrZSB1c2Ugb3VyIFBDQSBwZXJmb3JtZWQgYWJvdmUgdG8gaW50ZXJwcmV0IHdoeSBjZXJ0YWluIGluZGl2aWR1YWxzIGhhdmUgYmVlbiBwcm9qZWN0ZWQgd2VsbCBvciBwb29ybHkgaGVyZS4gVGhpcyBjb3VsZCBiZSBwYXJ0aWN1bGFybHkgaW5zaWdodGZ1bCBmb3IgdGhlIHRhaWxzLCBzaW5jZSBsYXJnZSBwcmljZSBhcmUgdW5kZXJlc3RpbWF0ZWQgaGVyZSBhbmQgbG93IHByaWNlcyBhcmUgb3ZlcmVzdGltYXRlZC4gV2Ugbm90ZSB0aGF0IG9uZSBzb2x1dGlvbiBjb3VsZCBiZSB0byBhZGQgbW9yZSB3ZWlnaHQgdG8gdGhlIGRhdGEgaW4gdGhlIHRhaWxzIHRvIHJlZ3VsYXJpc2UgdHJhaW5pbmcgc2V0LiAKCmBgYHtyfQpwbG90KHRlc3Rfc2V0JFByaWNlLGxtX3ByZWRpY3Rpb24sIHlsYWIgPSAiUHJlZGljdGVkIiwgeGxhYiA9IkFjdHVhbCIpICsgYWJsaW5lKGE9MCxiPTEpCm1vZGVsX3ZzX3Rlc3Q8LSBkYXRhX2ZyYW1lKGxtX3ByZWRpY3Rpb24sdGVzdF9zZXQkUHJpY2UpCgpyZXNpZHVhbF9sbSA8LSBtb2RlbF92c190ZXN0ICU+JSBtdXRhdGUoIlJlc2lkdWFsIiA9IChsbV9wcmVkaWN0aW9uIC0gdGVzdF9zZXQkUHJpY2UpKQpwbG90KHJlc2lkdWFsX2xtJGB0ZXN0X3NldCRQcmljZWAscmVzaWR1YWxfbG0kUmVzaWR1YWwsIHhsYWI9ICJBY3R1YWwiLCB5bGFiID0gIlJlc2lkdWFsIikKCnF1YW50aWxlKGFicyhyZXNpZHVhbF9sbSRSZXNpZHVhbCkpCmBgYAoKCgoqKjE3KSBJZiBzb21lb25lIGFzayB5b3Ugd2h5IHlvdSBkaWQgb25lIGdsb2JhbCBtb2RlbCBhbmQgbm90IG9uZSBtb2RlbCBwZXIgc3VwcGxpZXIsIHdoYXQgaXMgeW91ciBhbnN3ZXI/KioKCkJlbG93IHdlIGNvdW50IHRoZSBhbW91bnQgb2YgZGF0YSBwb2ludHMgd2UgaGF2ZSBmb3IgZWFjaCBzdXBwbGllci4gV2Ugc2VlIFN1cHBsaWVyIEMgb25seSBoYXMgMTQgZGF0YSBwb2ludHMsIHRodXMgcGVyZm9ybWluZyBhIG1vZGVsIHBlciBzdXBwbGllciBtYXkgbGVhZCB0byBvdmVyZml0dGluZyBhbmQgZmFpbCB0byBwcmVkaWN0IGZ1dHVyZSBvYnNlcnZhdGlvbnMgcmVsaWFibHkuIElmIHdlIHJlZ3VsYXJpc2VkIG91ciBkYXRhLCB3ZSBtYXkgYmUgYWJsZSB0byBwZXJmb3JtIHRoaXMgdHlwZSBvZiBtb2RlbC4gCgpgYGB7cn0KcmF3X2RhdGEgJT4lIGdyb3VwX2J5KFN1cHBsaWVyKSAlPiUgIGNvdW50KCkKYGBgCgoKKioxOCkgVGhlc2UgZGF0YSBjb250YWluZWQgbWlzc2luZyB2YWx1ZXMuIE9uZSByZXByZXNlbnRhdGl2ZSBpbiB0aGUgY29tcGFueSBzdWdnZXN0cyBlaXRoZXIgdG8gcHV0IDAgaW4gdGhlIG1pc3NpbmcgY2VsbHMgb3IgdG8gaW1wdXRlIHdpdGggdGhlIG1lZGlhbiBvZiB0aGUgdmFyaWFibGVzLiBDb21tZW50LiBGb3IgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB3aXRoIG1pc3NpbmcgdmFsdWVzLCBpdCBpcyBkZWNpZGVkIHRvIGNyZWF0ZSBhIG5ldyBjYXRlZ29yeSDigJxtaXNzaW5n4oCdLiBDb21tZW50LioqCgpJbXB1dGF0aW9uIHdpdGggdGhlIG1lZGlhbiBvciByZXBsYWNpbmcgdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggMCB3aWxsIGhhdmUgc2ltaWxhciByZXN1bHRzIHRvIG1lYW4gaW1wdXRhdGlvbiBpbiB0aGUgc2Vuc2UgdGhhdCByZXBsYWNpbmcgd2l0aCBhIGNvbnN0YW50IGFsdGVycyB0aGUgc3RydWN0dXJlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldC4gVGhlIHZhcmlhYmlsaXR5IGluIHRoZSBkYXRhIHdpbGwgYmUgcmVkdWNlZCBhbmQgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMgd2lsbCBiZSBpZ29ybmVkLiBUaHVzIHRoZSB2YXJpYW5jZSBlc3RpbWF0ZXMgd2lsbCB0ZW5kIHRvIGJlIHVuZGVyZXN0aW1hdGVkIGFuZCB0aGUgbWFnbml0dWRlIG9mIHRoZSBjb3JyZWxhdGlvbiBhbHNvIGRlY3JlYXNlLiBPdmVyYWxsLCB0aGUgdmFyaWFuY2UgYW5kIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSB3aWxsIGJlIGRpc3RvcnRlZCBidXQgdGhpcyBwcm9jZWR1cmUgY2FuIGdpdmUgc2F0aXNmYWN0b3J5IHJlc3VsdHMgaWYgbm90IHRvbyBtdWNoIGRhdGEgaXMgbWlzc2luZy4gCgpDcmVhdGluZyBhIG5ldyBjYXRlZ29yeSBmb3IgZWFjaCB2YXJpYWJsZSB3aGljaCBjb250YWlucyBvbmUgb3IgbW9yZSBtaXNzaW5nIHZhbHVlcyBjYW4gYmUgYW4gZWZmZWN0aXZlIHdheSBvZiB0YWNrbGluZyBtaXNzaW5nIHZhbHVlcy4gQSB2YXJpYWJsZSB3aWxsIGhhdmUgYW4gZXh0cmEgY2F0ZWdvcnkgaWYgYXQgbGVhc3Qgb25lIGluZGl2aWR1YWwgcG9zc2Vzc2VzIG1pc3NpbmcgZGF0YSBmb3IgdmFyaWFibGUgdGhpcyB2YXJpYWJsZS4gSXQgd2lsbCB0aGVyZWZvcmUgYmUgcG9zc2libGUgdG8gY29uZHVjdCB0aGUgTUNBIG9uIHRoZSBuZXcgZGF0YSB0YWJsZSBieSBjb25zaWRlcmluZyB0aGUgbWlzc2luZyBjYXRlZ29yaWVzIGluIHRoZSBzYW1lIHdheSBhcyB0aGUgb3RoZXIgY2F0ZWdvcmllcy4gSW4gcHJhY3RpY2UsIHRoaXMgZGV0ZWN0cyB0aGUgc3RydWN0dXJlIG9mIHRoZSBtaXNzaW5nIGRhdGEgaW4gdGhlIGRhdGFzZXQgYW5kIGVuYWJsZXMgdGhlIHN0cnVjdHVyZSBvZiB0aGUgbWlzc2luZyBkYXRhIHRvIGJlIHZpZXdlZC4gRXhhbWluaW5nIHRoZSBvcmdhbmlzYXRpb24gb2YgbWlzc2luZyBkYXRhIGFuZCB1bmRlcnN0YW5kaW5nIHdoeSBjZXJ0YWluIGluZGl2aWR1YWxzIGRpZCBub3QgYW5zd2VyIGEgZ3JvdXAgb2YgcXVlc3Rpb25zIGNhbiBiZSBpbmZvcm1hdGl2ZSBidXQgaXQgYWxzbyBsZWFkcyB0byBleGNlc3NpdmUgc3RydWN0dXJpbmcgb2YgdGhlIGRhdGFzZXQgYW5kIGNhbiBjb25jZWFsIHRoZSBpbmZvcm1hdGlvbiBoZWxkIHdpdGhpbiB0aGUgYW5zd2Vycy4gCg==